zoukankan      html  css  js  c++  java
  • 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
    ",ans1,ans2);
    	}
    	return 0;
    }
    
  • 相关阅读:
    C# 课堂总结2-数据类型及转换方式
    C# 课堂总结1-二进制转换
    C++
    C++ 程序设计语言
    VS编译器问题总结
    go 笔记
    SIP协议 会话发起协议(二)
    SIP协议 会话发起协议(一)
    201707 一些好的文章
    编程拾穗
  • 原文地址:https://www.cnblogs.com/orangee/p/9739039.html
Copyright © 2011-2022 走看看