链剖——BZOJ4196/Luogu2146 [Noi2015]软件包管理器

来源:互联网 发布:java游戏吧 编辑:程序博客网 时间:2024/06/05 05:40

题面:BZOJ4196 Luogu2146
首先软件包形成了一棵树,然后题中的软件包只有两种状态:装了与没被装
如果要装某个软件包,那么从这个包到根的软件包都要装
如果要卸某个软件包,那么以这个软件包为根的子树软件包都不能装
所以这道题实际要我们干什么:树上修改一条链的值,修改子树的值,求一条链的和,求子树和
然后发现要求的操作和洛谷的树剖模板题几乎一样?!
这里写图片描述
那么就把Luogu的模板拿来改一下就好了(逃
不过那是不可能的)两题题面还是有很大的不同的
这道NOI原题要求边询问边修改,这个倒不是难题,直接跟在询问后面好了
关于子树相关操作其实也很简单,dfs序之后节点x的整棵子树区间范围连续且确定在sx[x]~sx[x]+s[x]-1(sx[x]为x转到线段树里的编号,s[x]表示以x为根子树大小)
关键在于树剖之后的线段树处理的细节部分。。。(我卡了很久吧。。。
我是把软件包的状态看成0/1,0表示装了1表示没装(这样有利于链上操作而且子树操作也不受太大影响)
然后查的时候把1搞出来,边查边改,计算答案,细节很多

#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<iostream>#include<cstdlib>#include<string>#include<ctime>#include<queue>#include<climits>using namespace std;char c[11];int n,nedge=0,p[200001],nex[200001],head[200001];int fa[100001],deep[100001],s[100001],son[100001],top[100001];int sx[100001],xs[100001],ne=0;int lt[400001],rt[400001],t[400001],add[400001];inline void addedge(int a,int b){p[++nedge]=b;nex[nedge]=head[a];head[a]=nedge;}inline void dfs(int x,int Fa,int dep){    fa[x]=Fa;deep[x]=dep;s[x]=1;    for(int k=head[x];k;k=nex[k]){        if(p[k]==Fa)continue;        dfs(p[k],x,dep+1);s[x]+=s[p[k]];        if(!son[x]||s[p[k]]>s[son[x]])son[x]=p[k];    }}inline void dfss(int x,int bh){    top[x]=bh;sx[x]=++ne;xs[sx[x]]=x;    if(!son[x])return;    dfss(son[x],bh);    for(int k=head[x];k;k=nex[k])if(p[k]!=son[x]&&p[k]!=fa[x])dfss(p[k],p[k]);}inline void clean(int nod){//lazy标记下传    if(add[nod]==-1)return;    if(lt[nod]!=rt[nod]){        t[nod*2]=add[nod]*(rt[nod*2]-lt[nod*2]+1);        add[nod*2]=add[nod];        t[nod*2+1]=add[nod]*(rt[nod*2+1]-lt[nod*2+1]+1);        add[nod*2+1]=add[nod];    }    add[nod]=-1;}inline void build(int l,int r,int nod){    lt[nod]=l;rt[nod]=r;    if(l==r){t[nod]=add[nod]=1;return;}    int mid=l+r>>1;    build(l,mid,nod*2);build(mid+1,r,nod*2+1);    t[nod]=t[nod*2]+t[nod*2+1];add[nod]=1;}inline void xg(int i,int j,int nod,int w){    clean(nod);        if(lt[nod]>=i&&rt[nod]<=j){t[nod]=(rt[nod]-lt[nod]+1)*w;add[nod]=w;return;}    int mid=lt[nod]+rt[nod]>>1;    if(i<=mid)xg(i,j,nod*2,w);    if(j>mid)xg(i,j,nod*2+1,w);    t[nod]=t[nod*2]+t[nod*2+1];}inline int ssum(int i,int j,int nod){    clean(nod);    if(lt[nod]>=i&&rt[nod]<=j)return t[nod];    int mid=lt[nod]+rt[nod]>>1,ans=0;    if(i<=mid)ans+=ssum(i,j,nod*2);    if(j>mid)ans+=ssum(i,j,nod*2+1);    return ans;}inline int fsum(int x,int y){    int fx=top[x],fy=top[y],ans=0;    while(fx!=fy){        if(deep[fx]<deep[fy])swap(fx,fy),swap(x,y);        ans+=ssum(sx[fx],sx[x],1);xg(sx[fx],sx[x],1,0);//边查边改1        x=fa[fx];fx=top[x];    }    if(deep[x]>deep[y])swap(x,y);    ans+=ssum(sx[x],sx[y],1);xg(sx[x],sx[y],1,0);//边查边改2    return ans;}int main(){    scanf("%d",&n);    for(int i=1;i<n;i++){        int x;scanf("%d",&x);        addedge(x,i);addedge(i,x);    }    dfs(0,-1,1);dfss(0,0);build(1,n,1);    int m;scanf("%d",&m);    for(int i=1;i<=m;i++){        int x;scanf("%s%d",c+1,&x);        if(c[1]=='i')printf("%d\n",fsum(0,x));        else{            printf("%d\n",s[x]-ssum(sx[x],sx[x]+s[x]-1,1));            xg(sx[x],sx[x]+s[x]-1,1,1);//处理子树部分        }    }    return 0;}
原创粉丝点击