【2019省选模拟】—树(并查集+容斥)

先不考虑修改
考虑直接不好求,改成求 g c d gcd 不为1的个数

容斥一下就是一个质数时联通的点对个数-2个质数的积时联通的点的个数+3个质数积联通的点的个数……

发现实际上就是 μ \mu

联通的点的个数实际上很好求,用并查集维护一下, m e r g e merge 的时候加一个 s i z siz 的积就可以了

但每次不能直接重置,用一个指针维护一下回收被修改的 s i z siz f a fa ,并查集也只能按秩合并

考虑有了修改怎么做
因为修改的次数很少

可以先不加入被修改的满足的边,记录一下

然后每次暴力枚举修改的边加入
统计答案就是了

复杂度 O ( n n + ( n + q 2 ) l o g n ) O(n\sqrt n +(n+q^2)logn)
实际上 O ( n n ) O(n\sqrt n) 可以记录每个数最小的质因子直接优化成 O ( n l o g n ) O(nlogn)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
inline int read(){
	char ch=getchar();
	int res=0,f=1;
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=getchar();
	return res*f;
}
const int N=100005,M=1000005;
int pr[M],mu[M],vis[M],tot;
inline void init(){
	mu[1]=1;
	for(int i=2;i<=M-5;i++){
		if(!vis[i])pr[++tot]=i,mu[i]=-1;
		for(int j=1;j<=tot&&i*pr[j]<=M-5;j++){
			vis[i*pr[j]]=1;
			if(i%pr[j]==0)break;
			mu[i*pr[j]]=-mu[i];
		}
	}
	memset(vis,0,sizeof(vis));
}
vector<int> add[M];
ll res,ans[N];
int fa[N],siz[N],*stk[N<<1],pre[M],top,u[N],v[N],w[N],x[N],y[N],e[N],chan[N],now[N],n,m,q;
inline int find(int x){
	return fa[x]==x?x:find(fa[x]);
}
inline void merge(int u,int v){
	int f1=find(u),f2=find(v);
	if(siz[f1]<siz[f2])swap(f1,f2);
	stk[++top]=&siz[f1],pre[top]=siz[f1];
	res+=1ll*siz[f1]*siz[f2],siz[f1]+=siz[f2];
	stk[++top]=&fa[f2],pre[top]=fa[f2];
	fa[f2]=f1;
}
int main(){
	n=read();init();
	for(int i=1;i<n;i++)
		u[i]=read(),v[i]=read(),w[i]=read();
	q=read();
	for(int i=1;i<=q;i++){
		x[i]=e[i]=read(),y[i]=read(),chan[e[i]]=1;
	}
	for(int i=1;i<n;i++){
		for(int j=1;j*j<=w[i];j++){
			if(w[i]%j!=0)continue;
			if(mu[j]){
				if(chan[i])vis[j]=1;
				else add[j].pb(i);
			}
			if(j*j!=w[i]&&mu[w[i]/j]){
				if(chan[i])vis[w[i]/j]=1;
				else add[w[i]/j].pb(i);
			}
		}
	}
	for(int i=1;i<=q;i++){
		for(int j=1;j*j<=y[i];j++){
			if(y[i]%j==0)vis[j]=vis[y[i]/j]=1;
		}
	}
	for(int i=1;i<=n;i++)siz[i]=1,fa[i]=i;
	sort(e+1,e+q+1),m=unique(e+1,e+q+1)-e-1;
	for(int i=1;i<=1e6;i++){//cout<<i<<'\n';
		if(!mu[i])continue;res=0;
		for(int j=0;j<add[i].size();j++)
			merge(u[add[i][j]],v[add[i][j]]);
		int lst=top,p=res;
		for(int j=0;j<=q;j++){
			if(vis[i]){
				for(int k=1;k<=q;k++)now[x[k]]=w[x[k]];
				for(int k=1;k<=j;k++)now[x[k]]=y[k];
				for(int k=1;k<=m;k++){
					if(now[e[k]]%i==0)merge(u[e[k]],v[e[k]]);
				}
			}
			ans[j]+=mu[i]*res,res=p;
			while(top>lst)*stk[top]=pre[top],top--;
		}
		while(top)*stk[top]=pre[top],top--;
	}
	for(int i=0;i<=q;i++)cout<<ans[i]<<'\n';
}

猜你喜欢

转载自blog.csdn.net/qq_42555009/article/details/88917240