[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

*/

小结:好题好题,这个出成考试题非常妙啊,好想诶,就是有点难写。当然只是对我来讲,别人肯定分分钟切掉。

posted @ 2019-07-27 21:34  JZYshuraK_彧  阅读(215)  评论(0编辑  收藏  举报