您的位置:首页 > 其它

洛谷P3960 [NOIp2017]列队

2017-11-26 13:45 302 查看

题目地址

洛谷P3960

分析

80pts

对于q≤500的数据

与其相关的行的信息最多只有500行,我们离线给这些行编号存进一个数组,然后再用另一个数组记录最后一列的信息

那么我们知道暴力处理一次事件的复杂度为O(n),直接做就好了

对于所有xi=1的数据

显然修改信息只会在第一行和最后一列上进行,我们不妨把第一行和最后一列连起来看做一个序列,那我们所要做的就是从序列中取出一个元素插到序列末尾

于是就可以用Splay维护啦

代码

#include <iostream>
#include <cstdio>
#include <cctype>
#include <algorithm>

using namespace std;

inline int get()
{
char ch; int res = 0; bool Flag = false;
while (!isdigit(ch = getchar()) && ch != '-');
(ch == '-' ? Flag = true : res = ch ^ 48);
while (isdigit(ch = getchar()))
res = res * 10 + ch - 48;
return Flag ? -res : res;
}

typedef long long ll;

inline void put(ll x)
{
if (x > 9) put(x / 10);
putchar(x % 10 + 48);
}

const int Maxn = 0x3f3f3f3f;
const int N = 505, M = 6e5 + 5;

int n, m, q;

namespace Solve1
{
const int L = 5e4 + 5;
ll lst[L], fir
[L], Ans;
int x
, y
, b
;
int c, p;

inline void Work()
{
p = q;
for (int i = 1; i <= q; ++i)
x[i] = b[i] = get(), y[i] = get();

sort(b + 1, b + p + 1);
p = unique(b + 1, b + p + 1) - b - 1;
for (int i = 1; i <= p; ++i)
for (int j = 1; j < m; ++j)
fir[i][j] = (ll)(b[i] - 1) * m + j;
for (int i = 1; i <= n; ++i) lst[i] = (ll)i * m;

for (int i = 1; i <= q; ++i)
{
c = lower_bound(b + 1, b + p + 1, x[i]) - b;
put(Ans = y[i] < m ? fir[c][y[i]] : lst[x[i]]), putchar('\n');
for (int j = y[i]; j < m; ++j) fir[c][j] = fir[c][j + 1];
if (y[i] < m) fir[c][m - 1] = lst[x[i]];
for (int j = x[i]; j < n; ++j) lst[j] = lst[j + 1];
lst
= Ans;
}
}
};

namespace Solve2
{
int fa[M], lc[M], rc[M], sze[M];
ll val[M]; int rt, T;

inline void Push(int x)
{
sze[x] = sze[lc[x]] + sze[rc[x]] + 1;
}

inline void Rotate(int x)
{
int y = fa[x], z = fa[y];
int b = (lc[y] == x ? rc[x] : lc[x]);
fa[y] = x; fa[x] = z;
if (b) fa = y;
if (z) (lc[z] == y ? lc[z] : rc[z]) = x;
if (lc[y] == x) rc[x] = y, lc[y] = b;
else lc[x] = y, rc[y] = b;
Push(y);
}

inline bool Which(int x)
{
return lc[fa[x]] == x;
}

inline void Splay(int x, int tar)
{
while (fa[x] != tar)
{
if (fa[fa[x]] != tar)
Which(fa[x]) == Which(x) ? Rotate(fa[x]) : Rotate(x);
Rotate(x);
}

Push(x);
if (!tar) rt = x;
}

inline int Build(int Fa, int l, int r)
{
if (l > r) return 0;
int x = l + r >> 1;
fa[x] = Fa; sze[x] = 1;
lc[x] = Build(x, l, x - 1);
rc[x] = Build(x, x + 1, r);
Push(x);
return x;
}

inline int FindKth(int k)
{
int x = rt;
while (x)
{
if (k <= sze[lc[x]])
x = lc[x];
else
{
k -= sze[lc[x]] + 1;
if (k == 0) return x;
x = rc[x];
}
}
return 0;
}

inline int Delete(int k)
{
int x = FindKth(k - 1),
y = FindKth(k + 1);
Splay(x, 0); Splay(y, x);
int z = lc[y]; lc[y] = 0;
Push(y); Push(x);
return z;
}

inline void Insert(int z, int k)
{
int x = FindKth(k - 1),
y = FindKth(k);
Splay(x, 0); Splay(y, x);
lc[y] = z; fa[z] = y;
Push(y); Push(x);
}

inline void Work()
{
int x, y;
val[1] = val[T = n + m + 1] = Maxn;
for (int i = 1; i <= m; ++i) val[i + 1] = (ll)i;
for (int i = 2; i <= n; ++i) val[m + i] = (ll)i * m;
rt = Build(0, 1, T);

for (int i = 1; i <= q; ++i)
{
x = get(); y = get();
put(val[FindKth(y + 1)]), putchar('\n');
Insert(Delete(y + 1), T - 1);
}
}
}

int main()
{
//  freopen("phalanx.in", "r", stdin);
//  freopen("phalanx.out", "w", stdout);

n = get(); m = get(); q = get();
int x, y, z;
if (q <= 500)
Solve1::Work();
else
Solve2::Work();
//  fclose(stdin); fclose(stdout);
return 0;
}


[b]100pts

听说正解是树状数组,然而并不会,于是还是写了Splay

我们分别用两棵Splay树A,B维护最后一列的信息和其余信息,那么A中就用n个节点维护最后一列的每一个位置,而对于B,由于初始时的编号是连续的,一开始只需用n个节点维护每一行[1,m−1]列的信息,但也只需记录其第一列的编号

那么这样如何处理一次事件呢?

假设询问位置(x,y)的编号,首先我们能找到包含这个位置的节点u,它在第x行所管辖的列的范围为[l,r](可以通过sze数组计算得到)

我们先讨论一般情况,假设l<y<r,我们将其拆成三个节点u1,u2,u3分别管辖第x行的列[l,y−1],y,[y+1,r],那么节点u2的编号也可通过计算得到

然后把u2删除,将A中对应第x行的节点插到u3后面,再把u2插到A的末尾,就完成了一次事件的操作

注意y=l或者y=r的边界情况,并且如果y=m要直接在A中修改

由于每次事件至多只会增加两个节点,时间复杂度为均摊O(nlogn)

代码

感觉写得乱七八糟

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>

using namespace std;

const int N = 2e6 + 5;

const int S = 1 << 20;
char frd[S], *hed = frd + S;
const char *tal = hed;

inline char nxtChar()
{
if (hed == tal)
fread(frd, 1, S, stdin), hed = frd;
return *hed++;
}

inline int get()
{
char ch; int res = 0;
while (!isdigit(ch = nxtChar()));
res = ch ^ 48;
while (isdigit(ch = nxtChar()))
res = res * 10 + ch - 48;
return res;
}

typedef long long ll;
inline void put(ll x)
{
if (x > 9) put(x / 10);
putchar(x % 10 + 48);
}

int n, m, q, T;
ll val
, a
, sze
, num
;
int lc
, rc
, fa
, rt[2];

inline void Push(int x)
{
sze[x] = sze[lc[x]] + sze[rc[x]] + num[x];
}

inline void Rotate(int x)
{
int y = fa[x], z = fa[y];
int b = lc[y] == x ? rc[x] : lc[x];
fa[x] = z; fa[y] = x;
if (b) fa[b] = y;
if (z) (lc[z] == y ? lc[z] : rc[z]) = x;
if (lc[y] == x) rc[x] = y, lc[y] = b;
else lc[x] = y, rc[y] = b;
Push(y);
}

inline bool Which(int x)
{
return lc[fa[x]] == x;
}

inline void Splay(int x, int tar, int I)
{
while (fa[x] != tar)
{
if (fa[fa[x]] != tar)
Which(fa[x]) == Which(x) ? Rotate(fa[x]) : Rotate(x);
Rotate(x);
}

Push(x);
if (!tar) rt[I] = x;
}

inline int Build(int l, int r, int lst, int nb)
{
if (l > r) return 0;
int mid = l + r >> 1, x = ++T;
fa[x] = lst; num[x] = nb; val[x] = a[mid];
lc[x] = Build(l, mid - 1, x, nb);
rc[x] = Build(mid + 1, r, x, nb);
return Push(x), x;
}

inline int GetKth(ll k, int I)
{
int x = rt[I];
while (x)
{
if (k <= sze[lc[x]])
x = lc[x];
else
{
k -= sze[lc[x]] + num[x];
if (k <= 0) return x;
x = rc[x];
}
}
}

inline int Delete(ll k, int I)
{
int x = GetKth(k - 1, I),
y = GetKth(k + 1, I);
Splay(x, 0, I); Splay(y, x, I);
int z = lc[y];
lc[y] = 0; Push(y); Push(x);
return z;
}

inline void Insert(int z, ll k, int I)
{
int x = GetKth(k - 1, I),
y = GetKth(k, I);
Splay(x, 0, I); Splay(y, x, I);
lc[y] = z; fa[z] = y;
Push(y); Push(x);
}

int main()
{
//  freopen("phalanx.in", "r", stdin);
//  freopen("phalanx.out", "w", stdout);

n = get(); m = get(); q = get();

a[1] = a[n + 2] = -1;
for (int i = 1; i <= n; ++i) a[i + 1] = (ll)(i - 1) * m + 1;
rt[0] = Build(1, n + 2, 0, m - 1);
for (int i = 1; i <= n; ++i) a[i + 1] = (ll)i * m;
rt[1] = Build(1, n + 2, 0, 1);

ll k; int x, y, z, p;
while (q--)
{
x = get(); y = get();
if (y == m)
{
z = Delete(x + 1, 1);
put(val[z]), putchar('\n');
Insert(z, n + 1, 1);
}
else
{
k = (ll)x * (m - 1) + y;
p = z = GetKth(k, 0); Splay(z, 0, 0);
put(val[z] + k - sze[lc[z]] - 1), putchar('\n');
ll l = sze[lc[z]] + 1,
r = sze[lc[z]] + num[z];

if (l == k)
{
int tx = GetKth(l - 1, 0),
ty = GetKth(r + 1, 0);
Splay(tx, 0, 0); Splay(ty, tx, 0);
lc[ty] = 0; Push(ty); Push(tx);
num[p] = sze[p] = 1;
}
else
{
num[z] = k - l; Push(z);
val[p = ++T] = val[z] + num[z];
num[p] = sze[p] = 1;
}

if (k < r)
{
val[++T] = val[p] + 1;
num[T] = sze[T] = r - k;
Insert(T, k, 0);
}
Insert(Delete(x + 1, 1), (ll)(x + 1) * (m - 1), 0);
Insert(p, n + 1, 1);
}
}

//  fclose(stdin); fclose(stdout);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Splay 平衡树