欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

P5311-[Ynoi2011]成都七中【点分树,树状数组】

发布时间:2023/12/3 58 豆豆
生活随笔 收集整理的这篇文章主要介绍了 P5311-[Ynoi2011]成都七中【点分树,树状数组】 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

正题

题目链接:https://www.luogu.com.cn/problem/P5311


题目大意

给出nnn个点的一棵树,每个节点有一个颜色,mmm次询问提出区间[l,r][l,r][l,r]的点构成的生成子图中xxx所在连通块的颜色数。

1≤n,m,ai≤1051\leq n,m,a_i\leq 10^51n,m,ai105


解题思路

用点分树解决本题是很妙的想法。/bx

考虑点分树如何解决,对于一个询问l,r,xl,r,xl,r,x,如果xxx在点分树上的一个祖先yyy满足xxxyyy的路径都是[l,r][l,r][l,r]的点,那么此时在yyy的点分子树上的所有节点zzz都满足如果yyyzzz的路径都是[l,r][l,r][l,r]的节点,那么xxxzzz的也是。具体原因很好理解,因为两条路径重复的那一段路已经满足条件了。

那么我们此时就可以将一个条件拆分成两个条件挂在yyy上了,具体地我们对于每个询问找到xxx点分树上深度最小的祖先yyy满足xxxyyy的路径上都是[l,r][l,r][l,r]的节点,然后把这个询问挂在这个点上。

然后我们暴力枚举所有点然后处理它的点分树子树,假设现在枚举到xxx点,我们把它的所有儿子按照xxx到它们的路径上的最小值从大到小询问,然后挂在xxx点上的询问按照lll从大到小排序。

然后暴力遍历,记录每个颜色最小的mxmxmx表示yyy到这个颜色的点需要经过的路径最大值,然后把权值丢到树状数组上查询即可。

时间复杂度:O(nlog⁡2n)O(n\log^2 n)O(nlog2n)


code

#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define lowbit(x) (x&-x) using namespace std; const int N=1e5+10; struct edge{int to,next; }a[N<<1]; struct node{int x,fa,l,r; }; int n,m,tot,num,root,ls[N],c[N]; int siz[N],f[N],t[N],lat[N],ans[N]; vector<node> anc[N],son[N],q[N]; bool v[N]; void addl(int x,int y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;return; } void Groot(int x,int fa){siz[x]=1;f[x]=0;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa||v[y])continue;Groot(y,x);siz[x]+=siz[y];f[x]=max(f[x],siz[y]);}f[x]=max(f[x],num-siz[x]);if(f[x]<f[root])root=x;return; } void dfs(int x,int fa,int fr,int mi=1e9,int mx=-1e9){mi=min(x,mi);mx=max(x,mx);anc[x].push_back((node){x,fr,mi,mx});son[fr].push_back((node){x,fr,mi,mx});for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa||v[y])continue;dfs(y,x,fr,mi,mx);}return; } void Build(int x){int S=num;v[x]=1;dfs(x,0,x);for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(v[y])continue;num=(siz[y]>siz[x])?(S-siz[x]):siz[y];root=0;Groot(y,x);Build(root);}return; } bool cmp(node x,node y) {return x.l>y.l;} void Change(int x,int val){while(x<=n){t[x]+=val;x+=lowbit(x);}return; } int Ask(int x){int ans=0;while(x){ans+=t[x];x-=lowbit(x);}return ans; } int main() {scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%d",&c[i]);for(int i=1;i<n;i++){int x,y;x=i,y=i+1;scanf("%d%d",&x,&y);addl(x,y);addl(y,x);}f[0]=n+1;num=n;Groot(1,1);Build(root);for(int t=1;t<=m;t++){int l,r,x;scanf("%d%d%d",&l,&r,&x);for(int i=0;i<anc[x].size();i++)if(anc[x][i].l>=l&&anc[x][i].r<=r){q[anc[x][i].fa].push_back((node){x,t,l,r});break;}}for(int i=0;i<N;i++)lat[i]=n+1;for(int p=1;p<=n;p++){sort(q[p].begin(),q[p].end(),cmp);sort(son[p].begin(),son[p].end(),cmp);int z=0;for(int i=0;i<son[p].size();i++){node x=son[p][i];while(z<q[p].size()&&q[p][z].l>x.l){node y=q[p][z];ans[y.fa]=Ask(y.r);z++;}Change(lat[c[x.x]],-1);lat[c[x.x]]=min(lat[c[x.x]],x.r);Change(lat[c[x.x]],1);}while(z<q[p].size()){node y=q[p][z];ans[y.fa]=Ask(y.r);z++;}for(int i=0;i<son[p].size();i++){node x=son[p][i];Change(lat[c[x.x]],-1);lat[c[x.x]]=n+1;}}for(int i=1;i<=m;i++)printf("%d\n",ans[i]);return 0; }

总结

以上是生活随笔为你收集整理的P5311-[Ynoi2011]成都七中【点分树,树状数组】的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。