[ NOI 2015 ] 软件包管理器

xiaoxiao2022-07-14  98

[ NOI 2015 ] 软件包管理器

题目大意:思路分析:怎么套线段树?怎么算改变状态的数量? 代码总结


题目描述(luogu.org) 题目描述(codevs.cn)


题目大意:

这里有一些软件,下载它,必须先下载它的所有祖先;删除它,必须先删除它的所有儿子。 由于这是树形结构,而且又让进行序列操作,自然想到了树链剖分。数据范围( n<=10^5 ) 树剖可过。


思路分析:

怎么套线段树?
看上去不好套,但是仔细想想:如果用1表示已安装,0表示未安装,那么整个树就是一个零一串。对于安装操作,就是把x点到1号点的数值全部变为1。对于删除操作,就是把它的子树都变为0。
怎么算改变状态的数量?
对于安装操作,先算出x点到1号点的和,这也就是有几个数为一。再用x点的深度,减去这个数值,即为这条路径上0的个数。 对于删除操作,求出这个点子树和,还是这颗子树有一个树为一,即为答案。

具体细节看代码

代码

#include<iostream> #include<cstdio> using namespace std; #define N 150000 int inp(){ int Ans=0;bool o=false;char c; while((c=getchar())<'0'||c>'9')if(c=='-')o=true;Ans=c-48; while((c=getchar())>='0'&&c<='9')Ans=(Ans<<3)+(Ans<<1)+c-48; return o==true?-Ans:Ans; } struct Edge{int t,nxt;}edge[N<<1]; int tot,n,m,first[N]; int cnt,deep[N],fa[N],size[N],hson[N],top[N],id[N]; int sum[N<<2],add[N<<2]; char ch[20]; void build(int f,int t){ edge[++tot]=(Edge){t,first[f]},first[f]=tot, edge[++tot]=(Edge){f,first[t]},first[t]=tot; } void dfs1(int x,int dad) { fa[x]=dad,deep[x]=deep[dad]+1,size[x]=1;int maxson=-1; for(int i=first[x];i;i=edge[i].nxt) { int t=edge[i].t; if(t==dad)continue; dfs1(t,x); if(size[t]>maxson)maxson=size[t],hson[x]=t; size[x]+=size[t]; } } void dfs2(int x,int topfa) { id[x]=++cnt,top[x]=topfa; if(!hson[x])return; dfs2(hson[x],topfa); for(int i=first[x];i;i=edge[i].nxt) { int t=edge[i].t; if(t==fa[x]||t==hson[x])continue; dfs2(t,t); } } void pushup(int root){sum[root]=sum[root<<1]+sum[root<<1|1];} void pushdown(int root,int ln,int rn) { if(add[root]==1) { add[root<<1]=add[root<<1|1]=1; sum[root<<1]=ln,sum[root<<1|1]=rn; add[root]=0; } if(add[root]==2) { add[root<<1]=add[root<<1|1]=2; sum[root<<1]=0,sum[root<<1|1]=0; add[root]=0; } } int qsum(int L,int R,int l,int r,int root) { if(L>R)swap(L,R); if(L<=l&&r<=R){int x=sum[root];sum[root]=r-l+1,add[root]=1;return x;} int mid=(l+r)>>1,ans=0; pushdown(root,mid-l+1,r-mid); if(L<=mid)ans+=qsum(L,R,l,mid,root<<1); if(R>mid)ans+=qsum(L,R,mid+1,r,root<<1|1); pushup(root); return ans; } int Qsum(int L,int R,int l,int r,int root) { if(L>R)swap(L,R); if(L<=l&&r<=R){int x=sum[root];sum[root]=0,add[root]=2;return x;} int mid=(l+r)>>1,ans=0; pushdown(root,mid-l+1,r-mid); if(L<=mid)ans+=Qsum(L,R,l,mid,root<<1); if(R>mid)ans+=Qsum(L,R,mid+1,r,root<<1|1); pushup(root); return ans; } int main() { n=inp(); for(int t,i=1;i<n;i++) t=inp(),build(i+1,t+1); dfs1(1,0),dfs2(1,1); m=inp(); for(int i=1,x,f;i<=m;i++) { scanf("%s",ch),f=x=inp(),f++,x++; if(ch[0]=='i') { int ans=0; while(top[x]!=top[1]) { ans+=qsum(id[x],id[top[x]],1,n,1); x=fa[top[x]]; } ans+=qsum(id[x],id[1],1,n,1); cout<<deep[f]-ans<<endl; } if(ch[0]=='u') { int ans=Qsum(id[x],id[x]+size[x]-1,1,n,1); cout<<ans<<endl; } } }

总结

这个题的思路不难,也没有坑点,算是树剖入门变式题目。


NOIp2018 RP++ ~

转载请注明原文地址: https://www.6miu.com/read-4970712.html

最新回复(0)