弹球弹弹弹
题目大意:有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
*/
小结:好题好题,这个出成考试题非常妙啊,好想诶,就是有点难写。当然只是对我来讲,别人肯定分分钟切掉。