NOI2015 软件包管理器
题意:
给定一棵$n$节点的有根树,现在有两种操作.
$1)$给定$x$,询问$x$节点到根路径上没被标记的点的个数,并把路径上所有点标记.
$2)$给定$x$,询问$x$子树内被标记的点的个数,并把子树内所有点的标记撤销.
$n<=10^{5}$
题解:
题目要求维护点到根的路径以及点的子树的信息.是树链剖分的裸题.
复杂度为$O(nlog^{2}n)$
注意注意注意【重要的事情说三遍】
在判断重儿子有一句
if(sz[y]>sz[son[x]])son[x]=y;
如果编号从$1$开始没问题,$son[x]$初始为$0$,$sz[0]=0$.
但是题目所给节点的编号从$0$开始!!
所以$sz[0]$不是$0$!!!!所以很多点就没有重儿子了!!所以就超时了!!!
为了保险起见,尽量把节点的编号从$1$开始!
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<ctime> #include<cstdlib> #include<cmath> #include<string> #include<vector> #include<map> #include<queue> #include<bitset> #define ll long long #define debug(x) cout<<#x<<" "<<x<<endl; #define db(x,y)cout<<#x<<" "<<#y<<" "<<x<<" "<<y<<endl; using namespace std; inline void rd(int &res){ res=0;char c; while(c=getchar(),c<48); do res=(res<<1)+(res<<3)+(c^48); while(c=getchar(),c>=48); } inline void print(int x){ if(!x)return ; print(x/10); putchar((x%10)^48); } inline void sc(int x){ if(x<0){x=-x;putchar('-');} print(x); if(!x)putchar('0'); putchar('\n'); } inline void Max(int &x,int y){if(x<y)x=y;} inline void Min(int &x,int y){if(x>y)x=y;} const int M=1e5+5; int res=0,n,m,sz[M],w[M],tot=0,fa[M],top[M],son[M],head[M],ec=2,to[M],nxt[M]; int dep[M],L[M],R[M]; struct Seg{ int t[M<<2],f[M<<2]; void down(int l,int mid,int r,int p){ if(f[p]==-1)return; t[p<<1]=f[p]*(mid-l+1); t[p<<1|1]=f[p]*(r-mid); f[p<<1]=f[p<<1|1]=f[p]; f[p]=-1; } void build(int l,int r,int p){ f[p]=-1; if(l==r)return; int mid=l+r>>1; build(l,mid,p<<1),build(mid+1,r,p<<1|1); } void upd(int L,int R,int l,int r,int v,int p){ if(L==l&&R==r){ res+=t[p]; t[p]=v*(r-l+1); f[p]=v; return; } int mid=L+R>>1; down(L,mid,R,p); if(r<=mid)upd(L,mid,l,r,v,p<<1); else if(l>mid)upd(mid+1,R,l,r,v,p<<1|1); else upd(L,mid,l,mid,v,p<<1),upd(mid+1,R,mid+1,r,v,p<<1|1); t[p]=t[p<<1]+t[p<<1|1]; } }T; void ins(int a,int b){ to[ec]=b;nxt[ec]=head[a];head[a]=ec++; } void dfs(int x){ sz[x]=1; for(int i=head[x];i;i=nxt[i]){ int y=to[i]; dep[y]=dep[x]+1; dfs(y); sz[x]+=sz[y]; if(!son[x]||sz[y]>sz[son[x]])son[x]=y;//得到大儿子 } } void build(int x,int tp){ top[x]=tp; L[x]=w[x]=++tot; if(son[x])build(son[x],tp); for(int i=head[x];i;i=nxt[i]){ if(to[i]!=son[x])build(to[i],to[i]); } R[x]=tot; } int add_anc(int a){//得到a的祖先的路径 int sum=dep[a]; res=0; while(~a){//求出没有装的个数 并且把每个点都装上 T.upd(1,n,w[top[a]],w[a],1,1);//求出已经有的点的个 a=fa[top[a]]; } // printf("%d %d\n",sum,res); return sum-res; } int del_son(int a){//删除自己的所有儿子 res=0; T.upd(1,n,L[a],R[a],0,1); return res; } int main(){ rd(n);fa[0]=-1; char s[20];int i,j,a; for(i=1;i<n;i++){ rd(fa[i]); ins(fa[i],i); } dep[0]=1; dfs(0); build(0,0); rd(m); T.build(1,n,1); while(m--){ scanf("%s",s);rd(a); if(s[0]=='i'){ sc(add_anc(a)); } else{ sc(del_son(a)); } } return 0; }
$By\ LIN452$
$2017.06.07$