【CF925E】May Holidays

题目

题目链接:https://codeforces.com/problemset/problem/925/E
有一棵 \(n\) 个点以 \(1\) 为根的树,初始所有点都是白点。接下来有 \(m\) 次操作,每次操作给定一个数 \(k\),若 \(k>0\),则将第 \(k\) 个点从白点变成黑点;若 \(k<0\),则将第 \(k\) 个点从黑点变成白点。同时每个点给定一个值 \(a_i\)若一个白点子树内黑点个数大于 \(a_i\),则称该白点为关键点。求每次操作后关键点的个数。保证操作合法。
\(n,m\leq 10^5\)

思路

首先我们把所有点的权值设为 \(-a_i\),当一个在它子树内的点从白色变成黑色时,就把 \(a_i\) 加一,如果 \(a_i\) 大于 \(0\) 就会产生 \(1\) 的贡献。
发现一个点只会把他祖先节点的 \(a\) 更新。考虑树剖。然后根据 dfs 序把点分块。对于每一个块维护 \(\text{add},\text{cnt}_j\),分别表示这一个块所有的 \(a\) 增加的值,以及这个块中 \(a_i=j\) 的数的数量。
当我们修改点 \(x\) 时,假设是从白色变成黑色,那么我们先把 \(x\) 所在的块的 \(\text{cnt}\) 更新,并且更新 \(ans\)。然后往上跳重链,对于每一条重链,找到对应区间,整块就把 \(\text{add}\) 加一,零散的就暴力修改。
时间复杂度 \(O(n\sqrt{n}\log n)\)。空间复杂度 \(O(n\sqrt{n})\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=100010,M=320;
int fa[N],son[N],siz[N],id[N],rk[N],top[N];
int a[N],head[N],L[M],R[M],bel[N],add[M],cnt[M][N*2];
int n,m,T,ans,tot;
bool vis[N];

int read()
{
	int d=0; char ch=getchar();
	while (!isdigit(ch)) ch=getchar();
	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
	return d;
}

struct edge
{
	int next,to;
}e[N];

void adde(int from,int to)
{
	e[++tot]=(edge){head[from],to};
	head[from]=tot;
}

void dfs1(int x)
{
	siz[x]=1;
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		dfs1(v);
		siz[x]+=siz[v];
		if (siz[v]>siz[son[x]]) son[x]=v;
	}
}

void dfs2(int x,int tp)
{
	top[x]=tp; id[x]=++tot; rk[tot]=x;
	if (son[x]) dfs2(son[x],tp);
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (v!=son[x]) dfs2(v,v);
	}
}

void upd(int i,int v)
{
	if (!vis[i])
	{
		cnt[bel[i]][a[i]]--;
		if (a[i]+add[bel[i]]>N) ans--;
	}
	a[i]+=v;
	if (!vis[i])
	{
		cnt[bel[i]][a[i]]++;
		if (a[i]+add[bel[i]]>N) ans++;
	}
}

void update(int l,int r,int v)
{
	if (bel[rk[l]]==bel[rk[r]])
	{
		for (int i=l;i<=r;i++) upd(rk[i],v);
		return;
	}
	for (int i=l;i<=R[bel[rk[l]]];i++) upd(rk[i],v);
	for (int i=r;i>=L[bel[rk[r]]];i--) upd(rk[i],v);
	for (int i=bel[rk[l]]+1;i<bel[rk[r]];i++)
	{
		if (v<0) ans-=cnt[i][N+1-add[i]];
		add[i]+=v;
		if (v>0) ans+=cnt[i][N+1-add[i]];
	}
}

int main()
{
	memset(head,-1,sizeof(head));
	n=read(); m=read(); 
	for (int i=2;i<=n;i++) fa[i]=read(),adde(fa[i],i);
	for (int i=1;i<=n;i++) a[i]=N-read();
	tot=0;
	dfs1(1); dfs2(1,1);
	T=sqrt(n)+1;
	for (int i=1;i<=T;i++)
	{
		L[i]=R[i-1]+1; R[i]=min(i*T,n);
		for (int j=L[i];j<=R[i];j++)
			bel[rk[j]]=i,cnt[i][a[rk[j]]]++;
	}
	while (m--)
	{
		int x=read(),v=vis[x]?-1:1;
		vis[x]^=1; cnt[bel[x]][a[x]]-=v;
		if (a[x]+add[bel[x]]>N) ans-=v;
		for (int i=fa[x];i;i=fa[top[i]])
			update(id[top[i]],id[i],v);
		printf("%d ",ans);
	}
	return 0;
}
posted @ 2021-02-22 16:06  stoorz  阅读(54)  评论(0编辑  收藏  举报