题目大意:
给出N个点的一棵树,有Q次操作,任何操作和查询都是对于叶子节点来说的。
每一次操作:
+ v表示点v来了盗贼,
- v表示点v的盗贼走了,
我们的根节点是1,和根节点1相连接的点都有水源,否则就没有了水源。
每次操作之后,查询:用最少的边数去切割树边,使得所有盗贼所在的点都没有水,于此同时,希望割除的方案使得无辜的城市的个数最少。
问最少边数和最少无辜城市的个数。
思路:
①我们知道,如果一个城市(v)有了盗贼,如果我们只贪心第一个点(最小割除边数)的话,我们直接删除与1节点直接相连的那个节点u(u是v的父亲/祖先),即可,那么最多割除的边数也就是与1号节点直接相连的点的个数。
②那么贪心第二个点,我们如果存在一个节点u(与1直接相连的点),其子树中的叶子节点被盗贼占领了若干个点,那么对应我们这条割边希望尽可能的向下去割,使得部分没有必要阻止水源的城市免收伤害。很显然,我们若找到了这些个被攻占的点的Lca的话,那么直接割除这个Lca上边的那条边即可。
我们知道,一颗子树中若干个节点的Lca,其实就是Dfs序最小的那个点,和Dfs序最大的那个点的Lca
那么接下来的任务就确定了,我们每一次查询的时候,求一下原来无辜的城市的个数,再求一下更新之后无辜的城市的个数,然后对应做差一下就能够知道多了多少个无辜的城市。对应加一下就行。
③每一次取最小最大Dfs序的点我们用set维护一下就行。
那么整体思路就是:预处理一下Dfs序,然后对于每个查询,
如果是节点u(与1直接相连的边)的子树中的第一个被攻占的点,那么ans++【类推相反,就是ans--】;
对于第二个查询,我们求一下原来情况下无辜城市的个数,然后更新一下求一下当前更新后无辜城市的个数,然后做差更新ans2即可。
Ac代码:
#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
using namespace std;
#define N 100000+5000
void fre() {
freopen("gangsters.in","r",stdin);
freopen("gangsters.out","w",stdout);
}
set<int>ss[150000];
vector<int>mp[150000];
int cnt,tot;
int n,m;
int Id[150000];
int Re[150000];
int sum[150000];
int belong[150000];
int size[150000];
int L[150000];
int R[150000];
void Dfs(int u,int from,int root)
{
int flag=0;
size[u]=0;
L[u]=++cnt;
if(root!=0)belong[u]=root;
for(int i=0;i<mp[u].size();i++)
{
int v=mp[u][i];
if(v==from)continue;
flag=1;
if(u!=1)Dfs(v,u,root);
else Dfs(v,u,v);
size[u]+=size[v];
}
if(flag==0)size[u]=1,Id[u]=++tot,Re[tot]=u;
R[u]=cnt;
}
/************************************/
int p[150000][20];
int d[150000];
void dfs(int u,int from)
{
for(int i=0;i<mp[u].size();i++)
{
int v=mp[u][i];
if(v==from)continue;
d[v]=d[u]+1;
p[v][0]=u;
dfs(v,u);
}
}
void init()
{
for(int j=1;(1<<j)<=n;j++)
{
for(int i=1;i<=n;i++)
{
p[i][j]=p[p[i][j-1]][j-1];
}
}
}
int Lca(int x,int y)
{
if(d[x]>d[y])swap(x,y);
int f=d[y]-d[x];
for(int i=0;(1<<i)<=f;i++)
{
if((1<<i)&f)y=p[y][i];
}
if(x!=y)
{
for(int i=(int)log2(N);i>=0;i--)
{
if(p[x][i]!=p[y][i])
{
x=p[x][i];
y=p[y][i];
}
}
x=p[x][0];
}
return x;
}
/************************************/
int main()
{
fre();
while(~scanf("%d%d",&n,&m))
{
cnt=0,tot=0;
memset(L,0,sizeof(L));
memset(R,0,sizeof(R));
memset(Re,0,sizeof(Re));
memset(Id,0,sizeof(Id));
memset(sum,0,sizeof(sum));
memset(size,0,sizeof(size));
memset(belong,0,sizeof(belong));
for(int i=1;i<=n;i++)mp[i].clear(),ss[i].clear();
for(int i=2;i<=n;i++)
{
int x;scanf("%d",&x);
mp[i].push_back(x);
mp[x].push_back(i);
}
Dfs(1,-1,0);dfs(1,-1);init();
int A=0,B=0;
while(m--)
{
char op[5];
int leaf;
scanf("%s%d",op,&leaf);
if(op[0]=='+')
{
if(sum[belong[leaf]]==0)
{
A++;
ss[belong[leaf]].insert(Id[leaf]);
sum[belong[leaf]]++;
}
else
{
int yuan=size[Lca(Re[*ss[belong[leaf]].begin()],Re[*--ss[belong[leaf]].end()])]-sum[belong[leaf]];
sum[belong[leaf]]++;
ss[belong[leaf]].insert(Id[leaf]);
int after=size[Lca(Re[*ss[belong[leaf]].begin()],Re[*--ss[belong[leaf]].end()])]-sum[belong[leaf]];
B+=after-yuan;
}
}
else
{
if(sum[belong[leaf]]==1)
{
A--;
ss[belong[leaf]].erase(Id[leaf]);
sum[belong[leaf]]--;
}
else
{
int yuan=size[Lca(Re[*ss[belong[leaf]].begin()],Re[*--ss[belong[leaf]].end()])]-sum[belong[leaf]];
sum[belong[leaf]]--;
ss[belong[leaf]].erase(Id[leaf]);
int after=size[Lca(Re[*ss[belong[leaf]].begin()],Re[*--ss[belong[leaf]].end()])]-sum[belong[leaf]];
B+=after-yuan;
}
}
printf("%d %d\n",A,B);
}
}
}