【题解】 集合论 互测题 分块+欧拉序+压位

Editorial

此处认为 \(\omega = 64\)。\(n\) 与值域同数量级。

这题有个显然的树上待修莫队,但感觉很屑,复杂度也不是很好(\(O(n^{\frac{5}{3}})\)),就没打算写。

于是想啊想啊,想了两个小时,搞出来一个复杂度更优的做法:

考虑先对原树跑出来欧拉序,把欧拉序分块,设块大小 \(B\)。每隔 \(B\) 个预处理它到根节点的异或 bitset。预处理复杂度 \(O(\frac{n^2}{B})\)。

这样子,我们就可以在 \(O(\frac{n}{\omega}+B)\) 的时间得到任何点到根的异或 bitset。

显然,得到了 bitset 之后就可以 \(O(\frac{n}{\omega})\) 得到答案。

修改操作直接判断修改的点是否是某个点的祖先,是,就需要修改 bitset。复杂度 \(O(qB)\)。

复杂度 \(O(\frac{n^2}{B}+\frac{nq}{\omega}+qB)\),当 \(B=\sqrt \frac{n^2}{q}\) 取到最优 \(O(n\sqrt{q}+\frac{nq}{\omega})\)。

Code

我由于是考场代码,没有写欧拉序分块,而是直接 DFS 序分块,每 \(\sqrt{n}\) 个结点求一次。

查询是向上跳到第一个预处理过的结点,根据构造可以卡到 \(O(n^{\frac{3}{4}})\) 的跳双亲次数。

长链剖分后这个查询的复杂度应该是 \(O(\frac{n}{\omega}+n^{\frac{3}{4}})\)。

#include <bits/stdc++.h>

#define debug(...) fprintf(stderr ,__VA_ARGS__)
#define __FILE(x)\
	freopen(#x".in" ,"r" ,stdin);\
	freopen(#x".out" ,"w" ,stdout)
#define LL long long
#define ULL unsigned long long

const int MX = 1e5 + 23;

using namespace std;

int read(){
	char k = getchar(); int x = 0;
	while(k < '0' || k > '9') k = getchar();
	while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
	return x;
}

const int BSZ = 160;
int n ,m;
ULL f[720][MX / 64 + 10];

int head[MX] ,tot = 1;
struct edge{
	int node ,next;
}h[MX << 1];
void addedge(int u ,int v ,int flg = 1){
	h[++tot] = (edge){v ,head[u]} ,head[u] = tot;
	if(flg) addedge(v ,u ,false);
}

namespace FUCKLCA{
	int ecnt ,id[MX] ,seq[18][MX << 1] ,dfn ,antid[MX] ,where[MX];
	int lg2[MX << 1];
	void dfs(int x ,int f){
		id[x] = ++dfn;
		antid[dfn] = x;
		seq[0][++ecnt] = id[x];
		where[x] = ecnt;
		for(int i = head[x] ,d ; i ; i = h[i].next){
			if((d = h[i].node) == f) continue;
			dfs(d ,x);
			seq[0][++ecnt] = id[x];
		}
	}
	int LCA(int x ,int y){
		x = where[x] ,y = where[y];
		if(x > y) std::swap(x ,y);
		int len = lg2[y - x + 1];
		return antid[std::min(seq[len][x] ,seq[len][y - (1 << len) + 1])];
	}
	void main(){
		dfs(1 ,0);
		lg2[0] = -1;
		for(int i = 1 ; i < MX << 1 ; ++i)
			lg2[i] = lg2[i - 1] + (i == (i & -i));
		for(int i = 1 ; i < 18 ; ++i)
			for(int j = 1 ; j + (1 << i) - 1 <= ecnt ; ++j)
				seq[i][j] = std::min(seq[i - 1][j] ,seq[i - 1][j + (1 << (i - 1))]);
	}
}using FUCKLCA::LCA;

int dep[MX] ,hson[MX] ,fa[MX] ,sz[MX];
void dfs1(int x ,int f ,int dp){
	fa[x] = f ,dep[x] = dp ,sz[x] = 1;
	for(int i = head[x] ,d ; i ; i = h[i].next){
		if((d = h[i].node) == f) continue;
		dfs1(d ,x ,dp + 1) ,sz[x] += sz[d];
		if(dep[d] > dep[hson[x]]) hson[x] = d;
	}
}

ULL cur[MX / 64 + 10];
void obitset(ULL *A ,int len){
	for(int i = 1 ; i <= len ; ++i){
		if((A[i >> 6] >> (i & 63)) & 1){
			printf("%d," ,i);
		}
		// printf("%llu" ,(A[i >> 6] >> (i & 63)) & 1);
		if(i == len) printf("end\n");
	}
}

int a[MX];
int dfn ,setup[MX] ,id[MX] ,antid[MX] ,bid[MX] ,antibid[MX];
void dfs2(int x){
	cur[a[x] >> 6] ^= 1ull << (a[x] & 63);
	// printf("%d: " ,x); obitset(cur ,30000);
	id[x] = ++dfn;
	bid[x] = dfn / BSZ;
	antid[dfn] = x;
	if(dfn % BSZ == 0){
		antibid[dfn / BSZ] = x;
		setup[x] = true;
		for(int j = 0 ; j < MX / 64 + 1 ; ++j){
			f[bid[x]][j] = cur[j];
		}
	}
	if(hson[x]) dfs2(hson[x]);
	for(int i = head[x] ,d ; i ; i = h[i].next){
		if((d = h[i].node) == hson[x] || d == fa[x]) continue;
		dfs2(d);
	}
	cur[a[x] >> 6] ^= 1ull << (a[x] & 63);
}

int isanc(int anc ,int x){
	return id[anc] <= id[x] && id[x] < id[anc] + sz[anc];
}

void getit(int x){
	if(setup[x]){
		for(int i = 0 ; i < MX / 64 + 1 ; ++i){
			cur[i] ^= f[bid[x]][i];
		}
		return ;
	}
	cur[a[x] >> 6] ^= 1ull << (a[x] & 63);
	getit(fa[x]);
}

int query(int x ,int y ,int k){
	memset(cur ,0 ,sizeof cur);
	getit(x) ,getit(y);
	int lca = LCA(x ,y);
	cur[a[lca] >> 6] ^= 1ull << (a[lca] & 63);

	// obitset(cur ,30000);
	
	int all = 0 ,ans = 0;
	for(int i = 0 ; i < MX / 64 + 1 ; ++i){
		all += __builtin_popcountll(cur[i]);
	}
	if(all == 0){
		ans = -1;
		goto nmsl;
	}

	k = (k - 1) % all + 1;
	for(int i = 0 ; i < MX / 64 + 1 ; ++i){
		if(k - __builtin_popcountll(cur[i]) <= 0){
			for(int j = 0 ; j < 64 ; ++j){
				k -= (cur[i] >> j) & 1;
				if(!k){
					ans = i * 64 + j;
					goto nmsl;
				}
			}
		}
		k -= __builtin_popcountll(cur[i]);
	}
nmsl:

	return ans;
}

int main(){
	__FILE(set);
	n = read() ,m = read();
	for(int i = 1 ; i <= n ; ++i){
		a[i] = read();
	}
	for(int i = 1 ,u ,v ; i < n ; ++i){
		u = read() ,v = read();
		addedge(u ,v);
	}
	setup[0] = true;
	dfs1(1 ,0 ,1);
	dfs2(1);
	FUCKLCA::main();

	for(int i = 1 ,op ,x ,y ,k ; i <= m ; ++i){
		op = read();
		x = read();
		y = read();
		if(op == 1){
			for(int j = 1 ; j * BSZ <= n ; ++j){
				if(isanc(x ,antibid[j])){
					f[j][a[x] >> 6] ^= 1ull << (a[x] & 63);
					f[j][y >> 6] ^= 1ull << (y & 63);
				}
			}
			a[x] = y;
		}
		else{
			k = read();
			printf("%d\n" ,query(x ,y ,k));
		}
	}
	return 0;
}
上一篇:[html]自定义文件上传按钮


下一篇:「考试总结2021-04-06」 线代