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;
}

原文地址:https://www.cnblogs.com/orangee/p/9739039.html

时间: 2024-10-28 08:42:08

2016 NEERC, Northern Subregional Contest G.Gangsters in Central City(LCA)的相关文章

2015-2016 ACM-ICPC, NEERC, Northern Subregional Contest D:Distribution in Metagonia(构造)

http://codeforces.com/gym/100801/attachments 题意:给出一个数n(1 <= n <= 1e18),将 n 拆成 m 个整数,其中 m 必须是 2^x * 3^y 的形式,并且 x 和 y 不能被彼此整除, 输出 m 并将这些整数输出. 思路:Inspired by http://blog.csdn.net/snowy_smile/article/details/49852091 . 第一步:因为要求的 m 是 2^x * 3^y 的形式,所以如果 n

ACM ICPC 2011–2012, NEERC, Northern Subregional Contest J. John’s Inversions(合并排序求逆序数对数)

题目链接:http://codeforces.com/gym/100609/attachments 题目大意:有n张牌,每张牌有红色和蓝色两面,两面分别写了一些数字,同种颜色的任意两个数字若排在前面的数字比排在后面的数字大就叫做一对逆序数.求怎样排序得到的逆序数对最少. 解题思路:其中一种颜色的数字是顺序且这种颜色数字相同时对应的另一种颜色的数字是顺序时得到的逆序数对数最少.难点在于求逆序数对数.因为数量很大O(n^2)复杂度不能满足,这里根据合并排序的原理求解每个数字前面有多少个比它大的数字,

G. Gangsters in Central City

给出一棵$1$为根节点的含$n$个节点的树,叶子节点都是房屋,在一个集合里面添加房屋和移除房屋. 每一次添加和移除后,回答下面两个问题. 1.  使得已选房屋都不能从根节点到达,最少需要砍多少条边. 2.  在第$1$问最少砍去边的条件下,如何砍边使得从节点点开始走不能到达的非已选房屋数目最小,输出最小值. 对于100%的数据 , $2 ≤ n ≤ 10^5 , 1 ≤ q ≤ 10^5$ Solution : 首先观察到,第一问的答案. 非常容易证明的一个上边界是砍去所有合法的$u$和1的连边

Gym 101142G : Gangsters in Central City(DFS序+LCA+set)

题意:现在有一棵树,1号节点是水源,叶子节点是村庄,现在有些怪兽会占领一些村庄(即只占领叶子节点),现在要割去一些边,使得怪兽到不了水源.给出怪兽占领和离开的情况,现在要割每次回答最小的割,使得怪兽不与1号节点有联系,而且满足被阻隔的村庄最少.输出最小割与组少的被误伤的村庄. 思路:把与一号节点相邻的点看作祖先gfa,然后它们自己作为树的根节点,根节点保存了子树里叶子节点的个数.很显然一棵树我们要割的是这棵树里所有怪兽的LCA与父亲边.子数里所有怪兽的LCA=LCA(最小DFS序的怪兽点,最大D

模拟赛小结:2014-2015 ACM-ICPC, NEERC, Southern Subregional Contest

2014-2015 ACM-ICPC, NEERC, Southern Subregional Contest 2019年10月11日 15:30-20:30(Solved 6,Penalty 740) 国庆咸鱼十来天,回来又过了快一个星期,终于和队友约上了模拟赛.(周三拖周四,因为队(fei)友(zhai)们要跑1000米,又拖到周五QAQ) I:00:04.开场翻翻题目,机智如我很快找到一个贪心. D:00:36.看了看现场榜,有人交D.F和M,lh同学已经开F去了,xk同学说他M思路差不多

2013-2014 ACM-ICPC, NEERC, Eastern Subregional Contest PART (7/10)

\[2013-2014\ ACM-ICPC,\ NEERC,\ Eastern\ Subregional\ Contest\] \(A.Podracing\) \(B.The\ battle\ near\ the\ swamp\) 签到 //#pragma comment(linker, "/STACK:1024000000,1024000000") #include<bits/stdc++.h> using namespace std; function<void(

2018-2019 ICPC, NEERC, Southern Subregional Contest

目录 2018-2019 ICPC, NEERC, Southern Subregional Contest (Codeforces 1070) A.Find a Number(BFS) C.Cloud Computing(线段树) D.Garbage Disposal(模拟) E.Getting Deals Done(二分) F.Debate(贪心) H.BerOS File Suggestion(后缀自动机) I.Privatization of Roads in Berland(网络流)

Exchange 2016部署实施案例篇-04.Ex基础配置篇(中)

昨天更新了基础配置的上篇<Exchange 2016部署实施案例篇-04.Ex基础配置篇(上)>,欢迎各位老铁多多提出宝贵意见,非常感谢. 虚拟目录 自动发现配置 有的朋友可能知道,虽然在虚拟目录里有自动发现这个选项,但自动发现记录在图形化界面无法配置自动发现地址,如图所示 其实自动发现路径需要使用命令修改,我们先查下现在的自动发现写的是什么鬼 查看自动发现配置情况命令:Get-ClientAccessService | select Name,AutoDiscoverServiceInter

2016年上半年信息系统管理工程师考试上午真题(1)

通过信息系统管理工程师考试并获得证书的人员,能聘任对应技术岗位,能评中级职称.下面希赛软考学院为您整理了2016年上半年信息系统管理工程师上午真题,助准备参加考试的你一臂之力. 2016年上半年信息系统管理工程师考试上午真题(1-25题) ●CPU主要包含(1)等部件. A.运算器.控制器和系统总线 B.运算器.寄存器组和内存储器 C.运算器.控制器和寄存器组 D.控制器.指令译码器和寄存器组 ●按照(2),可将计算机分为RISC(精简指令集计算机)和CISC(复杂指令集计算机). A.规模和处