给出一棵$1$为根节点的含$n$个节点的树,叶子节点都是房屋,在一个集合里面添加房屋和移除房屋。

每一次添加和移除后,回答下面两个问题。

1.  使得已选房屋都不能从根节点到达,最少需要砍多少条边。

2.  在第$1$问最少砍去边的条件下,如何砍边使得从节点点开始走不能到达的非已选房屋数目最小,输出最小值。

对于100%的数据 , $2 ≤ n ≤ 10^5 , 1 ≤ q ≤ 10^5$

Solution :

  首先观察到,第一问的答案。

  非常容易证明的一个上边界是砍去所有合法的$u$和1的连边,其中合法的$u$表示既和$1$直接相连,且含有有已选被选叶子节点的$1$的直接儿子$u$

  显然,这样构造是可以切断所有的已选房屋的。

  如果不那么取,在一棵有被选叶子的子树中,如果不像上面一样构造,那么为了切断已选叶子,切边必须下移,显然,为了保证该子树已选叶子被截断,那么断边的数目必须递增。

  所有对于第一问,贪心策略成立。

  在第一问思考的基础上,我们进一步想,如果找到一个临界边,若砍掉其下面的边使得被选叶子节点被切断这些边数大于1了,而砍掉上面的那条边却恰好可以切断被选叶子节点,那么问题就解决了。

  这个临界边是这个子树里面被选叶子节点集合的LCA与父亲那一条连边。

  这就是第二问的贪心策略。

  如何求一个点集在树上的LCA(深度最大的公共祖先)呢?

  结论是:求出这个点集dfs序最小的一个点$u$,dfs序最大的一个点$v$,则$lca(u,v)$即为所求。

  这是因为dfs序覆盖的范围是一个连续的区间。

  假设有三个点,DFS序分别为a,p,b且a<p<b。那么我们断言LCA(a,p,b)=LCA(a,b)。

  这是因为:两个点的LCA,就是最近的同时包含它们的那个区间所代表的点。既然LCA(a,b)的区间包含了a和b,那也一定包含了位于a和b之间的p。

  因此,对于每任意三个点,只需要保存DFS序最大和最小的点。这个点集的LCA,也就是整个点集DFS序最小的和DFS序最大的这两个点的LCA。

至此,本题的理论部分就结束了,接下去就是实现的问题了。

首先对于每个叶子求出它对应属于根节点的那个子树zs[u],对于每个节点求出其size[]表示含有叶子节点的数目。

ret表示当前被标记的叶子节点数目,ans1表示第一问的答案,ans2表示被上述贪心策略覆盖总点数。

显然,最后的答案是ans1, ans2 - ret;

对于每个根节点子树开个set,维护当中被标记节点的dfs序,每次只需要求最大和最小即可。

对于每个+和-分别维护就行了(动态更新ans1,ans2)。

复杂度是$O(n log_2 n)$

# include <bits/stdc++.h>
using namespace std;
const int N=1e5+;
struct rec{ int pre,to; }a[N<<];
int n,Q,dfn[N],tot,head[N],g[N][],dep[N],size[N],zs[N],cnt[N];
struct node{ int x;};
bool operator < (node a,node b){return dfn[a.x]<dfn[b.x];}
set<node>st[N];
char s[];
void adde(int u,int v)
{
a[++tot].pre=head[u];
a[tot].to=v;
head[u]=tot;
}
void dfs1(int u,int fa)
{
dfn[u]=++dfn[]; g[u][]=fa,dep[u]=dep[fa]+; size[u]=;
bool leaf=;
for (int i=head[u];i;i=a[i].pre){
int v=a[i].to; if (v==fa) continue;
dfs1(v,u); leaf=; size[u]+=size[v];
}
if (leaf) size[u]=;
}
void dfs2(int u,int fa,int rt)
{
bool leaf=;
for (int i=head[u];i;i=a[i].pre) {
int v=a[i].to; if (v==fa) continue;
dfs2(v,u,rt); leaf=;
}
if (leaf) zs[u]=rt;
}
void init() {
for (int i=;i<=;i++)
for (int j=;j<=n;j++)
g[j][i]=g[g[j][i-]][i-];
}
int lca(int u,int v)
{
if (dep[u]<dep[v]) swap(u,v);
for (int i=;i>=;i--)
if (dep[g[u][i]]>=dep[v]) u=g[u][i];
if (u==v) return u;
for (int i=;i>=;i--)
if (g[u][i]!=g[v][i]) u=g[u][i],v=g[v][i];
return g[u][];
}
int main()
{
freopen("gangsters.in","r",stdin);
freopen("gangsters.out","w",stdout);
scanf("%d%d",&n,&Q);
for (int i=;i<=n;i++) {
int u; scanf("%d",&u);
adde(i,u); adde(u,i);
}
dfs1(,);init();
for (int i=head[];i;i=a[i].pre) dfs2(a[i].to,,a[i].to);
int ans1=,ans2=,ret=;
while (Q--) {
int d; scanf("%s%d",s,&d);
if (s[]=='+') {
ret++;
if (cnt[zs[d]]==) ans1++; cnt[zs[d]]++;
int pre=;
if (st[zs[d]].size()!=) {
int u=(*(st[zs[d]].begin())).x,v=(*(--st[zs[d]].end())).x;
pre=size[lca(u,v)];
}
st[zs[d]].insert((node){d});
int u=(*(st[zs[d]].begin())).x,v=(*(--st[zs[d]].end())).x;
int now=size[lca(u,v)];
ans2+=now-pre; printf("%d %d\n",ans1,ans2-ret);
} else {
ret--;
if (cnt[zs[d]]==) ans1--; cnt[zs[d]]--;
int u=(*(st[zs[d]].begin())).x,v=(*(--st[zs[d]].end())).x;
int pre=size[lca(u,v)];
st[zs[d]].erase((node){d});
int now=;
if (st[zs[d]].size()!=) {
u=(*(st[zs[d]].begin())).x,v=(*(--st[zs[d]].end())).x;
now=size[lca(u,v)];
}
ans2+=now-pre; printf("%d %d\n",ans1,ans2-ret);
}
}
return ;
}

G. Gangsters in Central City的更多相关文章

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

    G.Gangsters in Central City 题意:一棵树,节点1为根,是水源.水顺着边流至叶子.该树的每个叶子上有房子.有q个询问,一种为房子u被强盗入侵,另一种为强盗撤离房子u.对于每个 ...

  2. Codeforces Gym 101142 G Gangsters in Central City (lca+dfs序+树状数组+set)

    题意: 树的根节点为水源,编号为 1 .给定编号为 2, 3, 4, …, n 的点的父节点.已知只有叶子节点都是房子. 有 q 个操作,每个操作可以是下列两者之一: + v ,表示编号为 v 的房子 ...

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

    题意:现在有一棵树,1号节点是水源,叶子节点是村庄,现在有些怪兽会占领一些村庄(即只占领叶子节点),现在要割去一些边,使得怪兽到不了水源.给出怪兽占领和离开的情况,现在要割每次回答最小的割,使得怪兽不 ...

  4. Gym101142G Gangsters in Central City

    题目链接:https://cn.vjudge.net/problem/Gym-101142G 知识点: DFS序.LCA 题目大意: 给定一棵有根树(根为 \(1\)).每次修改叶子节点会被染成黑色( ...

  5. 2016-2017 ACM-ICPC, NEERC, Northern Subregional Contest

    A. Anniversary Cake 随便挑两个点切掉就好了. #include<bits/stdc++.h> using namespace std; const int Maxn=2 ...

  6. uva208 - Firetruck

    Firetruck The Center City fire department collaborates with the transportation department to maintai ...

  7. UVA208-Firetruck(并查集+dfs)

    Problem UVA208-Firetruck Accept:1733  Submit:14538 Time Limit: 3000 mSec  Problem Description The Ce ...

  8. 五、Pandas玩转数据

    Series的简单运算 import numpy as np import pandas as pd s1=pd.Series([1,2,3],index=['A','B','C']) print(s ...

  9. [转]MySQL Connector/C++(一)

    http://www.cnblogs.com/dvwei/archive/2013/04/18/3029464.html#undefined#undefined MySQL Connector/C++ ...

随机推荐

  1. <<C++ Primer>> 第 7 章 类

    术语表 第 7 章 类 抽象数据类型(abstract data type): 封装(隐藏)了实现细节的数据结构.    访问说明符(access specifier): 包括关键字 public 和 ...

  2. Django项目运行端口被占用

    error:以一种访问权限不允许的方式做了一个访问套接字的尝试,是8000端口被其他程序占用了,杀掉占用的程序就可以 (1)查找哪个进程占用了8000端口 `E:\sign_system\guest& ...

  3. CSS(上)

    目录 CSS(上) 什么是CSS? CSS的优点 CSS的引入方式 行内样式 内部样式 外部样式 CSS的两大特性 CSS选择器 基本选择器 组合选择器 更多选择器 选择器的优先级 CSS(上) 什么 ...

  4. 如何用纯 CSS 创作一个精彩的彩虹 loading 特效

    效果预览 在线演示 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/vjvoow 可交互视频教 ...

  5. 51nod 1251 Fox序列的数量 (容斥)

    枚举最多数字的出现次数$k$, 考虑其他数字的分配情况. 对至少$x$种数出现$\ge k$次的方案容斥, 有 $\sum (-1)^x\binom{m-1}{x}\binom{n-(x+1)k+m- ...

  6. selenium自动化测试工具模拟登陆爬取当当网top500畅销书单

    selenium自动化测试工具可谓是爬虫的利器,基本动态加载的网页都能抓取,当然随着大型网站的更新,也出现针对selenium的反爬,有些网站可以识别你是否用的是selenium访问,然后对你加以限制 ...

  7. Mysql 备份数据库方法及when using LOCK TABLES错误解决方法

    可以将以下代码保存为backup.bat,添加计划任务即可. @echo off ,,%" ,%" "D: -uname -pxxxx -P3306 --skip-loc ...

  8. 使输入框(input  & textarea)变为只可读状态readonly="readonly",禁用输入框disabled="disabled"

    使输入框变为只可读状态 readonly="readonly" <input class="select-city" placeholder=" ...

  9. 无法连接asp.net development server

    我的电脑重装了,在此之前,是可以进行调试的.并且将该解决方案拷到其他机器上能正常打开.并且正常调试.今天刚打开项目调试就提示“无法连接asp.net development server”,后来发现是 ...

  10. Linux和Windows双系统下Windows系统插入耳机没有声音

    我的笔记本装了Windows7和Debian双系统后,在Windows7下,插入耳机竟然没有声音. 按常规思路分析:首先考虑是耳机问题还是笔记本电脑问题.确定耳机没问题后问题就在笔记本身上了.而问题在 ...