设(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]);
}