LINK:树与异或
这种套路题还是得多写写。
第一问 直接树上莫队即可(不过这个板子也容易遗忘 推荐dfs序上搞 树分块总觉得比较难写...
第二问 询问树上路径上点权为z的倍数的点的个数.
Analysis:可以考虑暴力了。暴力枚举z 然后统计询问的答案。
不过每次要将z的倍数的点要加到数据结构中 然后就可以快速查询了 显然树状数组+dfs序就可以。
考虑这样做的复杂度 可以发现一个点最多被加入到树状数组里自己的因数个数次。
而一个数字的约数最多 \(n^{\frac{1}{2}}\),\(log^2\),\(n^{\frac{1}{3}}\). 有这几个数量级 精确一点的话第三个都是比较松的上界 。
所以 再乘以一个log 也是可以过的。
输出的一个小细节写挂了 写的时候一定要清醒哇.
再提供一个 分块分治的做法:对于z>sqrt(nlogn) 可以设数组 f[i][j]表示由根到i路径上为j的倍数的个数。
然后对于z>sqrt(nlogn) 此时使用树状数组。
前者 可以简单的发现是nsqrt(nlogn)的 后者 nn/sqrt(nlogn)logn=nsqrt(nlogn)的。
这个方法也是可以过的。
const int MAXN=100010;
int T,len,cnt,Q,n,ans,S,num,maxx;
int a[MAXN],w[MAXN],d[MAXN],ans1[MAXN],ans2[MAXN];
int f[MAXN][20],Log[MAXN],dfn[MAXN],las[MAXN],vis[MAXN];
int lin[MAXN],ver[MAXN<<1],nex[MAXN<<1],c[MAXN<<1],B[MAXN<<1],pos[MAXN<<1];
struct wy{int l,r,id,op;}t[MAXN],s[MAXN*3];
vector<int>g[MAXN];
inline void add(int x,int y)
{
ver[++len]=y;
nex[len]=lin[x];
lin[x]=len;
}
inline void dfs(int x,int fa)
{
d[x]=d[fa]+1;f[x][0]=fa;
dfn[x]=++cnt;pos[cnt]=x;
rep(1,Log[d[x]],i)f[x][i]=f[f[x][i-1]][i-1];
go(x)if(tn!=fa)dfs(tn,x);
las[x]=++cnt;pos[cnt]=x;
}
inline int LCA(int x,int y)
{
if(d[x]<d[y])swap(x,y);
fep(Log[d[x]],0,i)if(d[f[x][i]]>=d[y])x=f[x][i];
if(x==y)return x;
fep(Log[d[x]],0,i)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
return f[x][0];
}
inline int cmp(wy a,wy b){return B[a.l]==B[b.l]?a.r<b.r:a.l<b.l;}
inline int cmp1(wy a,wy b){return a.r<b.r;}
inline void del(int x){ans=ans-(--w[x]==0);}
inline void add(int x){ans=ans+(++w[x]==1);}
inline void change(int x)
{
if(vis[x])del(a[x]),vis[x]=0;
else add(a[x]),vis[x]=1;
}
inline void add1(int x,int y)
{
while(x<=num)
{
c[x]+=y;
x+=x&(-x);
}
}
inline int ask(int x)
{
int cnt=0;
while(x)
{
cnt+=c[x];
x-=x&(-x);
}
return cnt;
}
int main()
{
freopen("1.in","r",stdin);
get(T);
rep(1,T,TT)
{
rep(1,n,i)
{
lin[i]=vis[i]=0;
rep(1,Log[d[i]],j)f[i][j]=0;//可以发现 清空是必要的.
}
get(n);ans=num=maxx=cnt=len=0;
rep(1,n,i)
{
get(a[i]);
maxx=max(maxx,a[i]);
g[a[i]].pb(i);
w[a[i]]=0;
}
rep(2,n,i)
{
Log[i]=Log[i>>1]+1;
int get(x),get(y);
add(x,y);add(y,x);
}
dfs(1,0);
get(Q);
rep(1,Q,i)
{
ans1[i]=ans2[i]=0;
int get(x),get(y),get(z);
if(dfn[x]>dfn[y])swap(x,y);
int lca=LCA(x,y);
if(lca==x)t[i]=(wy){dfn[x],dfn[y],i,0};
else t[i]=(wy){las[x],dfn[y],i,lca};
s[++num]=(wy){y,z,i,1};
maxx=max(maxx,z);
if(lca==x){if(f[x][0])s[++num]=(wy){f[x][0],z,i,-1};}
else
{
s[++num]=(wy){x,z,i,1};
s[++num]=(wy){lca,z,i,-2};
}
}
S=(int)sqrt(2*n*1.0)+1;
rep(1,cnt,i)B[i]=(i-1)/S+1;
sort(t+1,t+1+Q,cmp);
int L=1,R=0;
rep(1,Q,i)
{
while(R<t[i].r)change(pos[++R]);
while(R>t[i].r)change(pos[R--]);
while(L<t[i].l)change(pos[L++]);
while(L>t[i].l)change(pos[--L]);
if(t[i].op)change(t[i].op);
ans1[t[i].id]=ans;
if(t[i].op)change(t[i].op);
}
//求树上路径和为z的倍数的个数 树状数组做.
sort(s+1,s+1+num,cmp1);
int flag=1;
rep(1,maxx,i)
{
if(s[flag].r==i)
{
for(int j=i;j<=maxx;j+=i)
{
for(ui k=0;k<g[j].size();++k)
{
int tn=g[j][k];
add1(dfn[tn],1);
add1(las[tn]+1,-1);
}
}
while(s[flag].r==i&&flag<=num)
{
ans2[s[flag].id]+=s[flag].op*ask(dfn[s[flag].l]);
if(s[flag].op==-2)if(a[s[flag].l]%i==0)++ans2[s[flag].id];
++flag;
}
for(int j=i;j<=maxx;j+=i)
{
for(ui k=0;k<g[j].size();++k)
{
int tn=g[j][k];
add1(dfn[tn],-1);
add1(las[tn]+1,1);
}
}
}
g[i].clear();
}
printf("Case #%d:\n",TT);
rep(1,Q,i)put(ans1[i]^ans2[i]);
}
return 0;
}