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

1|0正题

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


1|1题目大意

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

1n,m,ai105


1|2解题思路

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

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

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

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

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

时间复杂度:O(nlog2n)


1|3code

#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; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/15357537.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(36)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示