ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-03-09 14:02:58  阅读:168  来源: 互联网

标签:frac int 题解 复杂度 互测题 压位 1ull omega MX


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;
}

标签:frac,int,题解,复杂度,互测题,压位,1ull,omega,MX
来源: https://www.cnblogs.com/imakf/p/14505131.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有