[Nowcoder113E]弹球弹弹弹_线段树
弹球弹弹弹
题目大意:有n个位置,标号为1到n的整数,m次操作,第i次操作放置一个弹球在b[i] xor c[i-1]处,并询问b[i] xor c[i-1]处弹球个数c[i]每次操作后,在x处的弹球被弹到a[x],规定c[0]=0。
数据范围:1<=n,m<=500000。
题解:
这个题真的是,说起来容易写起来是真的恶心.....
首先要读题,每次操作是这样的:
先加进来一个球,然后查询当前位置的个数,接着直接把每个位置弹到对应位置。
显然,每个点有一条出边,这是一个基环内向森林的模型。
我们把询问分成两种情况讨论:第一种是查询不在环上的点,第二种是环上的点。
第一种怎么弄呢?
首先发现,如果一个球进了环就出不来了,所以我们只需要统计这个点子树内的情况(我们以基环树的环为根,每个数的根就是对应环上的点)。
发现,如果一个点子树内的点$u$满足加入树的时间$time_u$加上和当前点的距离$dep[u]-dep[now]$等于当前时间$nowtime$的话,这个点就可以被统计到。
故此把每个点的权值设为$time_x+dep_x$,只需要查询当前点的子树中,权值等于$nowtime+dep[x]$的点个数。
这个可以用线段树啊平衡树啊什么的实现,我这里用的是线段树。
具体地,我们对于每个权值开一棵线段树,最多需要开$maxtime + maxdep$棵。
线段树上每个节点维护其管辖区间里,所有权值等于当前线段树权值的点个数。
修改就直接在对应权值里修改就好了。
好,第一种情况我们就处理完了,复杂度是$O(nlogn)$。
看第二种情况。
第二种情况的答案贡献有两个渠道,第一个是本身修改就在环上修改的点,第二个是从树上走到环里的点。
第一个好弄,我们对每个环建立一个循环数组,环上每个点有个偏移量$skew_x$表示其距离环上拟定$0$号点的距离。
每次修改我们都直接修改到对应的节点上,就是修改到一次都没弹过的节点上,查询也上那儿查。
第一个就弄完了。
看看第二个:
第二个的话,就是修改是在树上修改,但是走到环上了。
怎么办呢?我们就弄一个$vector$叫$Fix_time$。
$Fix_i$表示所有在第$i$时刻的点集合。
对于一个在树上修改的点,我们把它扔进$Fix_{dep[x]+nowtime}$。
当处理完修改和查询之后,我们处理当前时刻的$Fix$数组。
每次只需要看一下,对应的点需要加到没有弹过的哪个环上节点就好了。
难写难调。
还有,这个题好像平衡树卡常,线段树秒过。
我写的线段树
代码:
这个是删掉所有调试注释的,方便取用。
#include <bits/stdc++.h> #define N 500010 using namespace std; struct Edge { int to[N << 1], nxt[N << 1], tot, head[N]; inline void add(int x, int y) { to[ ++ tot] = y; nxt[tot] = head[x]; head[x] = tot; } }G1,G2; int d[N], dep[N], qu[N], dfn[N], Q[N], vis[N], sk[N], sz[N], top[N]; int root[N << 1]; bool disp[N]; queue<int> q; vector<int> Round[N], Ans[N]; int cnt; vector<int> Fix[N << 1]; struct Segment_Tree { int ls, rs, sum; }a[N << 8]; char *p1, *p2, buf[100000]; #define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ ) int rd() { int x = 0, f = 1; char c = nc(); while (c < 48) { if (c == '-') f = -1; c = nc(); } while (c > 47) { x = (((x << 2) + x) << 1) + (c ^ 48), c = nc(); } return x * f; } int n; void toposort() { for (int i = 1; i <= n; i ++ ) { if (!d[i]) { vis[i] = -1; q.push(i); } } while (!q.empty()) { int x = q.front(); q.pop(); for (int i = G1.head[x]; i; i = G1.nxt[i]) { d[G1.to[i]] -- ; if (!d[G1.to[i]]) { q.push(G1.to[i]); vis[G1.to[i]] = -1; } } } } inline void pushup(int x) { int ls = a[x].ls, rs = a[x].rs; a[x].sum = 0; if (ls) { a[x].sum += a[ls].sum; } if (rs) { a[x].sum += a[rs].sum; } } void update(int x, int val, int l, int r, int &p) { if (!p) { p = ++cnt; } if (l == r) { a[p].sum ++ ; return; } int mid = (l + r) >> 1; if (x <= mid) { update(x, val, l, mid, a[p].ls); } else { update(x, val, mid + 1, r, a[p].rs); } pushup(p); } int query(int x, int y, int l, int r, int p) { if (!p) { return 0; } if (x <= l && r <= y) { return a[p].sum; } int mid = (l + r) >> 1, ans = 0; if (x <= mid) { ans += query(x, y, l, mid, a[p].ls); } if (mid < y) { ans += query(x, y, mid + 1, r, a[p].rs); } return ans; } void dfs_init(int p, int fa, int anc) { dfn[p] = ++dfn[0]; top[p] = anc; sz[p] = 1; dep[p] = dep[fa] + 1; for (int i = G2.head[p]; i; i = G2.nxt[i]) { if (G2.to[i] != fa && vis[G2.to[i]] == -1) { dfs_init(G2.to[i], p, anc); sz[p] += sz[G2.to[i]]; } } } int main() { n = rd(); for (int i = 1; i <= n; i ++ ) { qu[i] = rd(); G1.add(i, qu[i]); d[qu[i]] ++ ; } toposort(); for (int i = 1; i <= n; i ++ ) { if (vis[i]) { G2.add(qu[i], i); } } for (int i = 1; i <= n; i ++ ) { if (!vis[i] && !disp[i]) { vis[0] ++ ; int t = i; int now = 0; do { vis[t] = vis[0]; disp[t] = true; Round[vis[0]].push_back(t); Ans[vis[0]].push_back(0); now ++ ; sk[t] = now - 1; dfs_init(t, t, t); t = qu[t]; } while(t != i); } } int m = rd(); int lastans = 0; for (int i = 1; i <= m; i ++ ) { int x = rd(); if (vis[x] == -1) { update(dfn[x], 1, 1, n, root[i + dep[x]]); lastans = query(dfn[x], dfn[x] + sz[x] - 1, 1, n, root[i + dep[x]]); printf("%d\n", lastans); Fix[i + dep[x] - 2].push_back(x); } else { int Round_length = Round[vis[x]].size(); int now_position = ((sk[x] - i + 1) % Round_length + Round_length) % Round_length; Ans[vis[x]][now_position] ++ ; lastans = Ans[vis[x]][now_position]; printf("%d\n", lastans); } int len = Fix[i].size(); for (int j = 0; j < len; j ++ ) { int y = top[Fix[i][j]]; int Round_length = Round[vis[y]].size(); int now_position = ((sk[y] - i) % Round_length + Round_length) %Round_length; Ans[vis[y]][now_position] ++ ; } } return 0; }
这个嘛....是调的时候加的调试信息....博主只会用输出调试所以调完的代码都比较壮观,这个题恶心我两个小时特殊记录一下.....(有兴趣可以看看爽一爽顺便$diss$博主傻逼/xk)
#include <bits/stdc++.h> #define N 500010 using namespace std; struct Edge { int to[N << 1], nxt[N << 1], tot, head[N]; inline void add(int x, int y) { to[ ++ tot] = y; nxt[tot] = head[x]; head[x] = tot; } }G1,G2; int d[N], dep[N], qu[N], dfn[N], Q[N], vis[N], sk[N], sz[N], top[N]; int root[N << 1]; bool disp[N]; queue<int> q; vector<int> Round[N], Ans[N]; int cnt; vector<int> Fix[N << 1]; struct Segment_Tree { int ls, rs, sum; }a[N << 8]; char *p1, *p2, buf[100000]; #define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ ) int rd() { int x = 0, f = 1; char c = nc(); while (c < 48) { if (c == '-') f = -1; c = nc(); } while (c > 47) { x = (((x << 2) + x) << 1) + (c ^ 48), c = nc(); } return x * f; } int n; void toposort() { for (int i = 1; i <= n; i ++ ) { if (!d[i]) { vis[i] = -1; q.push(i); } } while (!q.empty()) { int x = q.front(); q.pop(); for (int i = G1.head[x]; i; i = G1.nxt[i]) { d[G1.to[i]] -- ; if (!d[G1.to[i]]) { q.push(G1.to[i]); vis[G1.to[i]] = -1; } } } } inline void pushup(int x) { int ls = a[x].ls, rs = a[x].rs; a[x].sum = 0; if (ls) { a[x].sum += a[ls].sum; } if (rs) { a[x].sum += a[rs].sum; } } void update(int x, int val, int l, int r, int &p) { // printf("A %d %d %d %d %d %d\n", x, val, l, r, p, a[p].sum); if (!p) { p = ++cnt; } // printf("B %d %d %d %d %d %d\n", x, val, l, r, p, a[p].sum); if (l == r) { a[p].sum ++ ; return; } // printf("C %d %d %d %d %d %d\n", x, val, l, r, p, a[p].sum); int mid = (l + r) >> 1; if (x <= mid) { update(x, val, l, mid, a[p].ls); } else { update(x, val, mid + 1, r, a[p].rs); } pushup(p); // printf("D %d %d %d %d %d %d\n", x, val, l, r, p, a[p].sum); } int query(int x, int y, int l, int r, int p) { if (!p) { return 0; } if (x <= l && r <= y) { return a[p].sum; } int mid = (l + r) >> 1, ans = 0; if (x <= mid) { ans += query(x, y, l, mid, a[p].ls); } if (mid < y) { ans += query(x, y, mid + 1, r, a[p].rs); } return ans; } void dfs_init(int p, int fa, int anc) { dfn[p] = ++dfn[0]; top[p] = anc; sz[p] = 1; dep[p] = dep[fa] + 1; for (int i = G2.head[p]; i; i = G2.nxt[i]) { if (G2.to[i] != fa && vis[G2.to[i]] == -1) { dfs_init(G2.to[i], p, anc); sz[p] += sz[G2.to[i]]; } } } int main() { n = rd(); for (int i = 1; i <= n; i ++ ) { qu[i] = rd(); G1.add(i, qu[i]); d[qu[i]] ++ ; } toposort(); for (int i = 1; i <= n; i ++ ) { if (vis[i]) { G2.add(qu[i], i); } } // for (int i = 1; i <= n; i ++ ) { // printf("%d ",vis[i]); // } // puts(""); for (int i = 1; i <= n; i ++ ) { if (!vis[i] && !disp[i]) { // printf("i-> %d\n", i); vis[0] ++ ; int t = i; int now = 0; do { // printf("t-> %d\n", t); vis[t] = vis[0]; disp[t] = true; Round[vis[0]].push_back(t); Ans[vis[0]].push_back(0); now ++ ; sk[t] = now - 1; dfs_init(t, t, t); t = qu[t]; // printf("t-> %d\n", t); } while(t != i); // while (t != i) { // vis[t] = vis[0]; // disp[t] = true; // Round[vis[0]].push_back(t); // Ans[vis[0]].push_back(0); // now ++ ; // sk[t] = now - 1; // dfs_init(t, t, t); // t = qu[t]; // } } } // puts("Fuck"); // printf("%d %d %d %d\n", sk[2], sk[4], sk[7], sk[10]); int m = rd(); int lastans = 0; for (int i = 1; i <= m; i ++ ) { int x = rd(); // x ^= lastans; // printf("i- %d\n", i); if (vis[x] == -1) { // printf("A\n"); // printf("%d\n", i + dep[x]); update(dfn[x], 1, 1, n, root[i + dep[x]]); lastans = query(dfn[x], dfn[x] + sz[x] - 1, 1, n, root[i + dep[x]]); printf("%d\n", lastans); // printf("Fix %d\n", i + dep[x] - 2); Fix[i + dep[x] - 2].push_back(x); } else { int Round_length = Round[vis[x]].size(); int now_position = ((sk[x] - i + 1) % Round_length + Round_length) % Round_length; // printf("%d %d\n", Round_length, now_position); Ans[vis[x]][now_position] ++ ; lastans = Ans[vis[x]][now_position]; printf("%d\n", lastans); } int len = Fix[i].size(); for (int j = 0; j < len; j ++ ) { // Fix[i][j]; int y = top[Fix[i][j]]; int Round_length = Round[vis[y]].size(); int now_position = ((sk[y] - i) % Round_length + Round_length) %Round_length; // printf("np %d\n", now_position); // printf("%d %d\n", Round_length, now_position); Ans[vis[y]][now_position] ++ ; } } return 0; } /* 6 1 2 1 3 3 6 20 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 6 6 */ /* 1 1 1 1 1 1 5 2 1 1 1 2 9 3 1 1 1 3 4 5 */
小结:好题好题,这个出成考试题非常妙啊,好想诶,就是有点难写。当然只是对我来讲,别人肯定分分钟切掉。
| 欢迎来原网站坐坐! >原文链接<