一道树剖的变形题
跟普通树剖不一样的是,每次安装/ 卸载软件后,需要把区间中的值统一改为 0 / 1 0/1 0/1 。而这个也并不难实现
s u m [ k ] sum[k] sum[k] 表示区间 k k k 中已安装的软件个数( 1 1 1 的个数),并不需要再开一个数组维护 lazy-tag,可以理解为将求和和 lazy-tag 合并了
每次要将区间赋值
0
/
1
0/1
0/1 时,就直接将
s
u
m
[
k
]
sum[k]
sum[k] 赋值为
0
/
(
r
−
l
+
1
)
0/(r-l+1)
0/(r−l+1),然后直接回溯。在访问的时候遇到
s
u
m
[
k
]
=
0
/
(
r
−
l
+
1
)
sum[k] = 0/(r-l+1)
sum[k]=0/(r−l+1) 时,就当做 lazy-tag 下放
关键代码:
inline void push_down(int k,int l,int r)
{
int mid=(l+r)>>1;
if(sum[k]==(r-l+1)) // 如果该区间是满的(全是1)
{//他的子区间也必定是满的
sum[k<<1]=(mid-l+1);
sum[k<<1|1]=(r-mid);
}
else if(!sum[k]) // 反之亦然
sum[k<<1]=sum[k<<1|1]=0;
}
void modify(int k,int l,int r,int x,int y,int v) // 将区间中所有元素赋值为v
{
if(x<=l && r<=y){sum[k]=(r-l+1)*v;return;}
push_down(k,l,r);
int mid=(l+r)>>1;
if(x<=mid)modify(k<<1,l,mid,x,y,v);
if(mid<y)modify(k<<1|1,mid+1,r,x,y,v);
push_up(k);
}
int query(int k,int l,int r,int x,int y,int v)// 询问等于v的元素个数
{
if(x<=l && r<=y)
{
if(v)return sum[k];
else return (r-l+1)-sum[k];
}
push_down(k,l,r);
int mid=(l+r)>>1,ret=0;
if(x<=mid)ret+=query(k<<1,l,mid,x,y,v);
if(mid<y)ret+=query(k<<1|1,mid+1,r,x,y,v);
return ret;
}
解决了这个问题之后,剩下的就是模板树剖啦
PS:代码里的节点编号为
1
∼
n
1\sim n
1∼n
#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int Maxn=100000+10,Maxm=400000+10;
int sum[Maxm],add[Maxm];
int s[Maxn],id[Maxn],son[Maxn];
int top[Maxn],f[Maxn],d[Maxn];
int n,m,idcnt;
vector <int> e[Maxn];
inline void push_up(int k)
{
sum[k]=sum[k<<1]+sum[k<<1|1];
}
inline void push_down(int k,int l,int r)
{
int mid=(l+r)>>1;
if(sum[k]==(r-l+1))
{
sum[k<<1]=(mid-l+1);
sum[k<<1|1]=(r-mid);
}
else if(!sum[k])
sum[k<<1]=sum[k<<1|1]=0;
}
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return s*w;
}
void modify(int k,int l,int r,int x,int y,int v)
{
if(x<=l && r<=y){sum[k]=(r-l+1)*v;return;}
push_down(k,l,r);
int mid=(l+r)>>1;
if(x<=mid)modify(k<<1,l,mid,x,y,v);
if(mid<y)modify(k<<1|1,mid+1,r,x,y,v);
push_up(k);
}
int query(int k,int l,int r,int x,int y,int v)
{
if(x<=l && r<=y)
{
if(v)return sum[k];
else return (r-l+1)-sum[k];
}
push_down(k,l,r);
int mid=(l+r)>>1,ret=0;
if(x<=mid)ret+=query(k<<1,l,mid,x,y,v);
if(mid<y)ret+=query(k<<1|1,mid+1,r,x,y,v);
return ret;
}
void dfs1(int x,int fa)
{
s[x]=1,f[x]=fa,d[x]=d[fa]+1;
for(int i=0;i<e[x].size();++i)
{
int y=e[x][i];
if(y==fa)continue;
dfs1(y,x),s[x]+=s[y];
if(s[y]>s[son[x]])son[x]=y;
}
}
void dfs2(int x,int topp)
{
top[x]=topp,id[x]=++idcnt;
if(!son[x])return;
dfs2(son[x],topp);
for(int i=0;i<e[x].size();++i)
{
int y=e[x][i];
if(y==f[x] || y==son[x])continue;
dfs2(y,y);
}
}
int ins(int x)// 将根节点到x的路径上的软件都装上
{
int ret=0;
while(top[x]!=1)
{
ret+=query(1,1,n,id[top[x]],id[x],0);
modify(1,1,n,id[top[x]],id[x],1);
x=f[top[x]];
}
ret+=query(1,1,n,id[1],id[x],0);
modify(1,1,n,id[1],id[x],1);
return ret;
}
int del(int x) // 将子树中已安装的全部删掉
{
int ret=query(1,1,n,id[x],id[x]+s[x]-1,1);
modify(1,1,n,id[x],id[x]+s[x]-1,0);
return ret;
}
int main()
{
// freopen("in.txt","r",stdin);
n=read();
for(int i=2;i<=n;++i)
{
int x=read()+1;
e[x].push_back(i);
e[i].push_back(x);
}
dfs1(1,0);
dfs2(1,1);
m=read();
while(m--)
{
char str[10];
scanf("%s",str);
int x=read()+1;
if(str[0]=='i')printf("%d\n",ins(x));
else printf("%d\n",del(x));
}
return 0;
}