codechef TREECNT2

(gcd(l...r))表示(l...r)路径上的(gcd)
(ans=[gcd(l...r)]=sum_{d|gcd(l...r)}vu(d))
(=sum_{i=1}^{1000000}vu(d)sum_{d|gcd(l...r)}1)
一个显然的想法:注意到只有(vu(d) eq 0)且被一个(gcd(l...r))整除的(d)才有用。
所以可以把所有(gcd(l...r))的约数(d)拿出来,建立一新树(T'),把边权为(d)的倍数的边插入(T'),然后统计每个连通块大小的二次方和。
由于最多会插入(2^d)条边,所以一次询问(O(n2^d))
有修改的情况显然可以维护(1000000)个LCT,然后在修改边权后在LCT上link,cut(2^d)次。
由于直接维护这么多个LCT不可承受,所以可以再次离线,枚举每个LCT,执行操作,再累加答案。
然而问题并没有要求强制在线。
这种有连通块的题,显然考虑并查集维护。
考虑可撤销并查集。
有一些边没有被修改过,显然先插入进去。
注意到(Q)很小。
每次修改时提取出在其他询问中被修改的边,插入它在当前位置的值,然后把当前位置的边插入到数据结构中。
最后再撤销。
最终时间复杂度(n2^d+2^d*Q^2*log_2n)

#include<bits/stdc++.h>
using namespace std;
#define N 2000010
#define M 2000010
#define int long long
int n,a[N],b[N],c[N],q,pr[M],f[N],sz[N],fc[N],vv[N],va[N],vg,vt[N],tp,k[N],x[N],cv[N],vp[N];
int p[M],vi[M],ct,vu[M];
int fd(int x){
	while(f[x])
		x=f[x];
	return x; 
}
struct no{
	int s,f,x,y;
}ss[M];
int mg(int x,int y){
	x=fd(x);
	y=fd(y);
	if(x==y)
		return 0;
	if(sz[x]>sz[y])
		swap(x,y);
	int ans=sz[x]*sz[y];
	ss[++tp]=((no){sz[y],f[x],x,y});
	sz[y]+=sz[x];
	f[x]=y;
	return ans;
}
void rb(int x){
	while(tp>x){
		no z=ss[tp];
		f[z.x]=z.f;
		sz[z.y]=z.s;
		tp--;
	}
}
vector<int>vc[M];
void si(){
	vu[1]=1;
	for(int i=2;i<M;i++){
		if(!vi[i]){
			p[++ct]=i;
			vu[i]=-1;
			pr[i]=i;
		}
		for(int j=1;i*p[j]<M;j++){
			vi[i*p[j]]=1;
			pr[i*p[j]]=p[j];
			if(i%p[j]==0)
				break;
			vu[i*p[j]]=-vu[i];
		}
	}
}
void dfs(int v,int x,int y,int t){
	if(y==ct+1){
		if(!t)
			vp[v]++;
		cv[v]++;
		if(t)
			vc[v].push_back(x);
		return;
	}
	dfs(v*fc[y],x,y+1,t);
	dfs(v,x,y+1,t);
}
signed main(){
	freopen("atoranta.in","r",stdin);
	freopen("atoranta.out","w",stdout);
	si();
	memset(vi,0,sizeof(vi));
	scanf("%lld",&n);
	for(int i=1;i<n;i++)
		scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
	for(int i=1;i<=n;i++)
		sz[i]=1;
	for(int i=1;i<n;i++){
		ct=0;
		int x=c[i];
		while(x!=1){
			if(!vv[pr[x]])
				fc[++ct]=pr[x];
			vv[pr[x]]=1;
			x/=pr[x];
		}
		dfs(1,i,1,1);
		for(int j=1;j<=ct;j++)
			vv[fc[j]]=0;
	}
	int ans=0;
	for(int i=1;i<=n;i++)
		sz[i]=1;
	for(int i=1;i<M;i++)
		if(vc[i].size()&&vu[i]){
			for(int j=0;j<vc[i].size();j++)
				ans+=vu[i]*mg(a[vc[i][j]],b[vc[i][j]]);
			rb(0);
		}
	printf("%lld
",ans);
	scanf("%lld",&q);
	for(int i=1;i<=q;i++){
		scanf("%lld%lld",&k[i],&x[i]);
		ct=0;
		int y=x[i];
		while(y!=1){
			if(!vv[pr[y]])
				fc[++ct]=pr[y];
			vv[pr[y]]=1;
			y/=pr[y];
		}
		for(int j=1;j<=ct;j++)
			vv[fc[j]]=0;
		dfs(1,i,1,0);
		vi[k[i]]=1;
	}
	for(int i=1;i<=n;i++)
		if(vi[i]){
			ct=0;
			int y=c[i];
			while(y!=1){
				if(!vv[pr[y]])
					fc[++ct]=pr[y];
				vv[pr[y]]=1;
				y/=pr[y];
			}
			for(int j=1;j<=ct;j++)
				vv[fc[j]]=0;
			dfs(1,i,1,0);
		}
	ans=0;
	int ct=0,ok=0;
	for(int i=1;i<M;i++){
		if(cv[i]&&vu[i]){
			ct+=q*q;
			ans=0;
			for(int j=0;j<vc[i].size();j++)
				if(!vi[vc[i][j]])
					ans+=vu[i]*mg(a[vc[i][j]],b[vc[i][j]]);
			for(int j=1;j<=q;j++)
				va[j]+=ans;
			int tt=tp;
			for(int j=1;j<=q;j++)
				vt[k[j]]=c[k[j]];
			if(vp[i]){
				for(int j=1;j<=q;j++){
					vt[k[j]]=x[j];
					ans=0;
					for(int l=1;l<=q;l++)
						if(vt[k[l]]%i==0){
							ok=1;
							ans+=vu[i]*mg(a[k[l]],b[k[l]]);
						}
					rb(tt);
					va[j]+=ans;
				}
			}
			rb(0);
		}
	}
	//printf("%d
",ok);
	for(int i=1;i<=q;i++)
		printf("%lld
",va[i]);
}
原文地址:https://www.cnblogs.com/ctmlpfs/p/14092130.html