测试地址:May Holidays
题目大意:一个个人的公司,除了号外每个人都有一个直属上司,为的下属当且仅当的直属上司是或的直属上司是的下属。每个人都有一个承受阈值,当一个人的下属中有严格大于个人休假而他没有休假时,他就会不开心。次操作(改动一个人的休假状态),求每次操作后不开心的人数。
做法:本题需要用到分块+虚树。
思来想去好像没有什么奇妙的数据结构能在或内解决这样的问题,于是想到分块。
考虑对询问分块,使用这种方法的条件是,任意一个前缀内询问的影响可以在时间内求出,而处理单独一个块内询问的复杂度应该为均摊一次(其中为块大小)。显然第一个条件可以满足,打标记再扫一遍即可。而对于第二个条件,我们可以先构建所有询问点的链并,实际上就是一棵虚树,只不过号点一定要存在在虚树中(因为是链并),显然构建的复杂度是。然后对于虚树上相邻的两个点之间的那些点,受到块内询问的影响都是相同的,于是考虑预处理出每一对相邻点之间链上那些点的一些值。令为点在该块询问之前的询问中受到的影响,为点在处理当前询问时点所受到的新的影响,那么显然当且点没有被标记(即不处于休假状态)时,点对答案有贡献,上式可以写成,于是我们只需对每一条虚树上的边,求出链上所有没被标记的点的,排序后存下来,那么在变化时,因为一次最多变化,所以指针最多移动一次,而在虚树上更新的次数是一次。那么就能达到预处理(实际上可以用基数排序优化到,但懒得写了),一个询问均摊的时间复杂度了。这就符合第二个条件了。
于是我们就解决了本题,时间复杂度为,实测当时可以通过此题。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,t[100010],op[100010];
int fa[100010][20]={0},tot=0,first[100010]={0},dep[100010]={0};
int tim=0,pos[100010],w[100010],ans;
int p[100010],totp,top,st[100010],pre[100010];
int totsiz,L[100010],R[100010],to[100010],s[100010],cnt[100010];
int news[100010],newsp[100010];
bool vis[100010]={0};
struct edge
{
int v,next;
}e[100010];
void insert(int a,int b)
{
e[++tot].v=b;
e[tot].next=first[a];
first[a]=tot;
}
void dfs(int v)
{
pos[v]=++tim;
dep[v]=dep[fa[v][0]]+1;
for(int i=first[v];i;i=e[i].next)
dfs(e[i].v);
}
void count(int v)
{
w[v]=0;
for(int i=first[v];i;i=e[i].next)
{
count(e[i].v);
w[v]+=w[e[i].v];
if (vis[e[i].v]) w[v]++;
}
if (!vis[v]&&w[v]>t[v]) ans++;
}
int lca(int a,int b)
{
if (dep[a]<dep[b]) swap(a,b);
for(int i=18;i>=0;i--)
if (dep[fa[a][i]]>=dep[b])
a=fa[a][i];
if (a==b) return a;
for(int i=18;i>=0;i--)
if (fa[a][i]!=fa[b][i])
a=fa[a][i],b=fa[b][i];
return fa[a][0];
}
bool cmp(int a,int b)
{
return pos[a]<pos[b];
}
bool cmpw(int a,int b)
{
return a<b;
}
void findup(int x,int y)
{
if (x==y) return;
if (!vis[x]) s[++totsiz]=t[x]-w[x];
findup(fa[x][0],y);
}
void build()
{
int newsiz=totp;
sort(p+1,p+totp+1,cmp);
for(int i=1;i<totp;i++)
p[++newsiz]=lca(p[i],p[i+1]);
p[++newsiz]=1;
totp=newsiz,newsiz=0;
sort(p+1,p+totp+1,cmp);
for(int i=1;i<=totp;i++)
if (i==1||p[i]!=p[i-1])
p[++newsiz]=p[i];
totp=newsiz;
top=0;
for(int i=1;i<=totp;i++)
{
while(top&&lca(st[top],p[i])!=st[top]) top--;
if (top) pre[p[i]]=st[top];
else pre[p[i]]=0;
st[++top]=p[i];
}
totsiz=0;
for(int i=2;i<=totp;i++)
{
int v=p[i],siz=totsiz;
L[v]=totsiz+1;
findup(fa[v][0],pre[v]);
R[v]=totsiz;
totsiz=siz;
if (L[v]>R[v]) continue;
to[v]=L[v]-1;
sort(s+totsiz+1,s+R[v]+1,cmpw);
for(int j=L[v];j<=R[v];j++)
{
if (j==L[v]||s[j]!=s[j-1])
{
s[++totsiz]=s[j];
if (s[j]<0) to[v]=totsiz;
cnt[totsiz]=1;
}
else cnt[totsiz]++;
}
R[v]=totsiz;
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=2;i<=n;i++)
{
scanf("%d",&fa[i][0]);
insert(fa[i][0],i);
}
for(int i=1;i<=18;i++)
for(int j=1;j<=n;j++)
fa[j][i]=fa[fa[j][i-1]][i-1];
dfs(1);
for(int i=1;i<=n;i++)
scanf("%d",&t[i]);
for(int i=1;i<=m;i++)
scanf("%d",&op[i]);
int B=(int)(sqrt(m)+1)<<1;
for(int i=1;i<=m;i+=B)
{
int l=i,r=min(m,i+B-1);
ans=0;
memset(vis,0,sizeof(vis));
for(int j=1;j<l;j++)
vis[abs(op[j])]=!vis[abs(op[j])];
count(1);
totp=0;
for(int j=l;j<=r;j++)
p[++totp]=abs(op[j]);
build();
memset(news,0,sizeof(news));
memset(newsp,0,sizeof(newsp));
for(int j=l;j<=r;j++)
{
int v=abs(op[j]),add=(op[j]>0)?1:-1;
if (!vis[v]&&newsp[v]>t[v]-w[v]) ans--;
vis[v]=!vis[v];
if (!vis[v]&&newsp[v]>t[v]-w[v]) ans++;
while(v)
{
if (v!=abs(op[j])) newsp[v]+=add;
news[v]+=add;
if (v!=abs(op[j]))
{
if (!vis[v]&&newsp[v]-add<=t[v]-w[v]&&newsp[v]>t[v]-w[v])
ans++;
if (!vis[v]&&newsp[v]-add>t[v]-w[v]&&newsp[v]<=t[v]-w[v])
ans--;
}
if (L[v]>R[v]||!pre[v])
{
v=pre[v];
continue;
}
if (add>0)
{
if (to[v]!=R[v]&&news[v]>s[to[v]+1])
{
to[v]++;
ans+=cnt[to[v]];
}
}
else if (to[v]!=L[v]-1)
{
if (news[v]<=s[to[v]])
{
ans-=cnt[to[v]];
to[v]--;
}
}
v=pre[v];
}
printf("%d ",ans);
}
}
return 0;
}