2016 NEERC, Northern Subregional Contest G.Gangsters in Central City(LCA)

G.Gangsters in Central City

题意:一棵树,节点1为根,是水源。水顺着边流至叶子。该树的每个叶子上有房子。有q个询问,一种为房子u被强盗入侵,另一种为强盗撤离房子u。对于每个询问,要求给出最小的阀门数来阻断水流向强盗所在房子,且在阀门数最小的情况下求最小的误伤房子数(即没被入侵却被断水的房子)。
思路:观察可发现,与根相连的子树都是独立的,因此有每有一颗这样的子树里有强盗,则ans1++,每有一颗这样的子树强盗全部撤离则ans1--;因此要维护的是误伤数ans2,我们对每一个与根相连的子树独立处理。要想最小化ans2,那阀门一定要设置在强盗所在房子的LCA的上边,则当前误伤数为该LCA为根的子树叶节点数减去强盗数,再减去原来的误伤数,即可用差值更新ans2。对于求某一子树内所有强盗的LCA,需要知道一个结论:若干个节点的LCA为其中dfs序最大与最小的两节点的LCA。因此可以开若干个set维护每个子树内强盗节点的dfs序。

#include<algorithm>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<set>
#define dd(x) cout<<#x<<" = "<<x<<" "
#define de(x) cout<<#x<<" = "<<x<<endl
#define pb push_back
#define all(x) x.begin(),x.end()
using namespace std;
const int maxn=1e5+10;
int head[maxn],cnt=0;
struct Edge
{
	int v,ne;
} edge[maxn<<1];
void add(int u,int v)
{
	edge[++cnt].ne=head[u];
	edge[cnt].v=v;
	head[u]=cnt;
}
int tot=0,fa[maxn][32],dep[maxn],sz[maxn],id[maxn],rk[maxn];//sz记录叶节点数
vector<int> tree;
void dfs(int f,int u,int deep)
{
	id[u]=++tot;
	rk[tot]=u;
	dep[u]=deep;
	sz[u]=edge[head[u]].ne==0;
	fa[u][0]=f;
	if (f==1)
		tree.pb(id[u]);
	for (int i=1; i<=22; ++i)
		fa[u][i]=fa[fa[u][i-1]][i-1];
	for (int i=head[u]; i; i=edge[i].ne)
	{
		int v=edge[i].v;
		if (v==f)
			continue;
		dfs(u,v,deep+1);
		sz[u]+=sz[v];
	}
}
int lca(int u,int v)
{
	if (dep[u]<dep[v])
		swap(u,v);
	for (int i=22,d=dep[u]-dep[v]; i>=0; --i)
		if (d&(1<<i))
			u=fa[u][i];
	if (u==v)
		return u;
	for (int i=22; i>=0; --i)
		if (fa[u][i]!=fa[v][i])
			u=fa[u][i],v=fa[v][i];
	return fa[u][0];
}
set<int> st[maxn];
int gan[maxn],man[maxn];//gan为强盗数,man为误伤数
int main()
{
	freopen("gangsters.in","r",stdin);
	freopen("gangsters.out","w",stdout); 
	int n,q;
	scanf("%d%d",&n,&q);
	for (int i=2; i<=n; ++i)
	{
		int v;
		scanf("%d",&v);
		add(i,v);
		add(v,i);
	}
	dfs(1,1,1);
	int ans1=0,ans2=0;
	while (q--)
	{
		char op[2];
		int u;
		scanf("%s%d",op,&u);
		int num=upper_bound(all(tree),id[u])-tree.begin();//通过u的dfs序判断u在哪个子树内
		if (op[0]=='+')
		{
			st[num].insert(id[u]);
			int mn=*st[num].begin(),mx=*(--st[num].end()),a=lca(rk[mx],rk[mn]);
			if (!gan[num])
				ans1++;
			gan[num]++;
			ans2+=sz[a]-gan[num]-man[num];
			man[num]=sz[a]-gan[num];
		}
		else
		{
			st[num].erase(lower_bound(all(st[num]),id[u]));
			gan[num]--;
			if (!gan[num])
			{
				ans1--;
				ans2-=man[num];
				man[num]=0;
			}
			else
			{
				int mn=*st[num].begin(),mx=*(--st[num].end()),a=lca(rk[mx],rk[mn]);				
				ans2+=sz[a]-gan[num]-man[num];
				man[num]=sz[a]-gan[num];
			}	
		}
		printf("%d %d\n",ans1,ans2);
	}
	return 0;
}
posted @ 2018-10-03 02:24  __orange  阅读(174)  评论(0编辑  收藏  举报