Discription
You are given a tree with N nodes. The tree nodes are numbered from 1 to N. Each node has an integer weight.
We will ask you to perform the following operation:
- u v : ask for how many different integers that represent the weight of nodes there are on the path from u to v.
Input
In the first line there are two integers N and M. (N <= 40000, M <= 100000)
In the second line there are N integers. The i-th integer denotes the weight of the i-th node.
In the next N-1 lines, each line contains two integers u v, which describes an edge (u, v).
In the next M lines, each line contains two integers u v, which means an operation asking for how many different integers that represent the weight of nodes there are on the path from u to v.
Output
For each operation, print its result.
Example
Input: 8 2 105 2 9 3 8 5 7 7 1 2 1 3 1 4 3 5 3 6 3 7 4 8 2 5 7 8
Output: 4 4
原来不仅子树询问可以树上莫队啊,路径询问也可以! (树上莫队板子题)
对于路径询问,我们对于每个点x要记录两个dfs_clock,一个是进入这个点的(设为B[x]),一个是出这个点的(设为E[x])。
接下来按照询问点(u,v)分类讨论(假设 B[u] < B[v]):
1.u是v的祖先:这种情况下直接询问dfs序区间 [B[u],B[v]] ,只有在(u,v)路径上的点在其中恰好出现1次,其他的点要么没出现要么出现2次,可以直接判出来。
2.u,v无直接祖先关系:这种情况下需要询问 [E[u],B[v]] ,仍然满足上一种情况的性质(LCA除外,LCA在这种计算方式下会被算到0次,所以要最后加上),所以类似处理。
所以把询问放到dfs序上直接莫队就好了。
这里再补充一些我yy出来的这种计算dfs序方法的一些性质:
1.对于每个点x,[B[x],E[x]]构成了这颗子树的dfs序区间,所有该子树中的点在其中出现2次,其他点在其中出现0次;
2.对于任意两个点 x,y , [B[x],E[x]] 与 [B[y],E[y]] 要么是包含关系,要么是相离关系,并且是包含当且仅当x,y其中一个是另一个的祖先。
3.(上述莫队有关的性质)
4.(欢迎补充)
由于近期要准备NOI啦,所以代码的正确率非常重要,于是现在每次提交之前尽量都要自己写个暴力写个数据生成器拍一下啦(希望能坚持好习惯23333)。
(顺便贴一下暴力和数据生成代码吧2333)
暴力:
#include<bits/stdc++.h> #define ll long long using namespace std; #define pb push_back const int maxn=40005; vector<int> g[maxn]; int n,m,a[maxn],num[maxn],ky; int dep[maxn],f[maxn],dc,c[maxn]; void dfs(int x,int fa){ f[x]=fa; for(int i=g[x].size()-1,to;i>=0;i--){ to=g[x][i]; if(to!=fa) dep[to]=dep[x]+1,dfs(to,x); } } inline int calc(int x,int y){ int an=0; dc++; while(x!=y){ if(dep[x]>dep[y]){ if(c[a[x]]!=dc) c[a[x]]=dc,an++; x=f[x]; } else{ if(c[a[y]]!=dc) c[a[y]]=dc,an++; y=f[y]; } } return an+(c[a[x]]!=dc); } inline void solve(){ int uu,vv; for(int i=1;i<=m;i++){ scanf("%d%d",&uu,&vv); printf("%d ",calc(uu,vv)); } } int main(){ freopen("data.in","r",stdin); freopen("ans.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",a+i),num[i]=a[i]; sort(num+1,num+n+1); int ky=unique(num+1,num+n+1)-num-1; for(int i=1;i<=n;i++) a[i]=lower_bound(num+1,num+ky+1,a[i])-num; int uu,vv; for(int i=1;i<n;i++){ scanf("%d%d",&uu,&vv); g[uu].pb(vv),g[vv].pb(uu); } dfs(1,0); solve(); return 0; }
数据生成器(虽然直接随机挂父亲的树深是期望log的,,,但是反正正解和树深也没啥关系):
#include<bits/stdc++.h> #define ll long long using namespace std; int main(){ freopen("data.in","w",stdout); srand(time(0)); int n=40000,m=100000; cout<<n<<' '<<m<<endl; for(int i=1;i<=n;i++) printf("%d ",rand()); puts(""); for(int i=2;i<=n;i++) printf("%d %d ",i,rand()%(i-1)+1); while(m--) printf("%d %d ",rand()%n+1,rand()%n+1); return 0; }
正解:
#include<bits/stdc++.h> #define ll long long using namespace std; #define pb push_back const int maxn=80005; vector<int> g[maxn]; struct node{ int L,R,bl,num,A; bool operator <(const node &u)const{ return bl==u.bl?((bl&1)?R<u.R:R>u.R):bl<u.bl; } }q[100005]; int n,a[maxn],num[maxn],cnt[maxn],sz,f[40005][23],ci[233],m; int ans[100005],dy[maxn],dc,B[maxn],E[maxn],dep[maxn],l,r,now; bool p[maxn]; void dfs(int x,int fa){ f[x][0]=fa; for(int i=1;ci[i]<=dep[x];i++) f[x][i]=f[f[x][i-1]][i-1]; dy[++dc]=x,B[x]=dc; for(int i=g[x].size()-1,to;i>=0;i--){ to=g[x][i]; if(to!=fa) dep[to]=dep[x]+1,dfs(to,x); } dy[++dc]=x,E[x]=dc; } inline int LCA(int x,int y){ if(dep[x]<dep[y]) swap(x,y); int D=dep[x]-dep[y]; for(int i=0;ci[i]<=D;i++) if(ci[i]&D) x=f[x][i]; if(x==y) return x; for(int i=log(dep[x])/log(2)+1;i>=0;i--) if(ci[i]<=dep[x]&&f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } inline void Get(int L,int R){ while(l>L){ l--; if(!p[dy[l]]){ p[dy[l]]=1; if(cnt[a[dy[l]]]==0) now++; cnt[a[dy[l]]]++; } else{ p[dy[l]]=0; cnt[a[dy[l]]]--; if(cnt[a[dy[l]]]==0) now--; } } while(r<R){ r++; if(!p[dy[r]]){ p[dy[r]]=1; if(cnt[a[dy[r]]]==0) now++; cnt[a[dy[r]]]++; } else{ p[dy[r]]=0; cnt[a[dy[r]]]--; if(cnt[a[dy[r]]]==0) now--; } } while(l<L){ if(!p[dy[l]]){ p[dy[l]]=1; if(cnt[a[dy[l]]]==0) now++; cnt[a[dy[l]]]++; } else{ p[dy[l]]=0; cnt[a[dy[l]]]--; if(cnt[a[dy[l]]]==0) now--; } l++; } while(r>R){ if(!p[dy[r]]){ p[dy[r]]=1; if(cnt[a[dy[r]]]==0) now++; cnt[a[dy[r]]]++; } else{ p[dy[r]]=0; cnt[a[dy[r]]]--; if(cnt[a[dy[r]]]==0) now--; } r--; } } inline void solve(){ int uu,vv; for(int i=1;i<=m;i++){ scanf("%d%d",&uu,&vv); if(B[uu]>B[vv]) swap(uu,vv); if(E[uu]>E[vv]) q[i]=(node){B[uu],B[vv],(B[uu]-1)/sz+1,i,0}; else q[i]=(node){E[uu],B[vv],(E[uu]-1)/sz+1,i,LCA(uu,vv)}; } sort(q+1,q+m+1),l=1,r=0; for(int i=1;i<=m;i++){ Get(q[i].L,q[i].R); ans[q[i].num]=now+(q[i].A&&!cnt[a[q[i].A]]); } } int main(){ // freopen("data.in","r",stdin); // freopen("data.out","w",stdout); ci[0]=1; for(int i=1;i<=20;i++) ci[i]=ci[i-1]<<1; scanf("%d%d",&n,&m),sz=sqrt(n<<1); for(int i=1;i<=n;i++) scanf("%d",a+i),num[i]=a[i]; sort(num+1,num+n+1); int ky=unique(num+1,num+n+1)-num-1; for(int i=1;i<=n;i++) a[i]=lower_bound(num+1,num+ky+1,a[i])-num; int uu,vv; for(int i=1;i<n;i++){ scanf("%d%d",&uu,&vv); g[uu].pb(vv),g[vv].pb(uu); } dfs(1,0); solve(); for(int i=1;i<=m;i++) printf("%d ",ans[i]); return 0; }