题意
给一棵带权树,多次询问路径上出现次数超过一半的数。
分析
- dfs序建主席树,维护的就是根到某个节点这段路径的值域情况。
- 因为题目所求的不是一般的众数,而是出现次数大于一半的,所以在主席树上可以直接二分,看两个子树的值域哪个大于一半,就走哪个子树,如果都为一半,返回-1。
- 树上主席树的查询不同于序列上的主席树,不是子树做差,而是子树相加减去两倍LCA对应的树,然后这样减去之后会把LCA对应的点也给减去,所以要根据LCA点权判断再加上。
- 有一道类似的题目,UVALive7831。
- 比赛时一直莫队莫队,最后还是莫不出来,太容易思想江化了。
代码
#include <bits/stdc++.h> using namespace std; const int N=1e6+50; struct Edge{ int v,next; }e[N*2]; int cnt,head[N],a[N]; int n,qs,u,v; int fa[N][30],d[N],pw[30]; void init(){ cnt=0; memset(head,-1,sizeof(head)); pw[0]=1; for(int i=1;i<=20;i++){ pw[i]=pw[i-1]*2; } } void add(int u,int v){ e[cnt]=Edge{v,head[u]}; head[u]=cnt++; e[cnt]=Edge{u,head[v]}; head[v]=cnt++; } #define mid (l+r)/2 int tr[N],sum[N*30],ls[N*30],rs[N*30],tot; void update(int pre,int &rt,int l,int r,int v){ rt=++tot; ls[rt]=ls[pre]; rs[rt]=rs[pre]; sum[rt]=sum[pre]+1; if(l<r){ if(v<=mid){ update(ls[pre],ls[rt],l,mid,v); }else{ update(rs[pre],rs[rt],mid+1,r,v); } } } int query(int u,int v,int lca,int l,int r,int k,int lc){ if(l==r){ return l; } int ltmp=sum[ls[u]]+sum[ls[v]]-2*sum[ls[lca]]+(a[lc]>=l && a[lc]<=mid ?1:0); int rtmp=sum[rs[u]]+sum[rs[v]]-2*sum[rs[lca]]+(a[lc]>mid && a[lc]<=r ?1:0); if(ltmp*2>k){ return query(ls[u],ls[v],ls[lca],l,mid,k,lc); }else if(rtmp*2>k){ return query(rs[u],rs[v],rs[lca],mid+1,r,k,lc); }else{ return -1; } } void dfs(int u){ for(int i=1;pw[i]<=d[u];i++){ fa[u][i]=fa[fa[u][i-1]][i-1]; } update(tr[fa[u][0]],tr[u],1,n,a[u]); for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].v; if(v==fa[u][0]){ continue; } fa[v][0]=u; d[v]=d[u]+1; dfs(v); } } int lca(int x,int y){ if(d[x]<d[y]){ swap(x,y); } int tmp=d[x]-d[y]; for(int i=0;pw[i]<=tmp;i++){ if(tmp&pw[i]){ x=fa[x][i]; } } if(x==y){ return x; } for(int i=19;i>=0;i--){ if(fa[x][i]!=fa[y][i]){ x=fa[x][i]; y=fa[y][i]; } } return fa[x][0]; } int main(){ // freopen("wo.txt","r",stdin); scanf("%d%d",&n,&qs); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); } init(); for(int i=1;i<n;i++){ scanf("%d%d",&u,&v); add(u,v); } dfs(1); while(qs--){ scanf("%d%d",&u,&v); int lc=lca(u,v); int len=d[u]+d[v]-2*d[lc]+1; int ans=query(tr[u],tr[v],tr[lc],1,n,len,lc); printf("%d\n",ans); } return 0; }