洛谷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; }
相关文章推荐
- 洛谷P3960 [NOIP2017] 列队
- NOIP2017 列队
- 洛谷P3953 [NOIP2017] 逛公园(最短路+拓扑序+DP)
- NOIP 2017 Senior 6 - 列队
- [NOIp 2017]列队
- NOIP 2017 列队
- 【NOIP2017DAY2T3 【NOIP2017提高组正式赛】列队 】(动态开点+n棵线段树)
- NOIP2017 列队 题解报告【56行线段树】
- 洛谷3958 noip2017 奶酪
- 【NOIP2017】列队(Splay)
- NOIP2017 D1T3 列队
- NOIP2017:列队
- 【NOIP2017】列队(Splay)
- NOIP 2017 Day2 题3:列队 线段树
- NOIP2017:列队
- [NOIp 2017]列队
- [NOIP2017模拟][洛谷3938]斐波那契
- [NOIp2017 Day2 T3] 列队phalanx(线段树 / 平衡树)
- 【NOIP 2017】列队
- [Noip2017] 列队