G. Gangsters in Central City
Posted ljc20020730
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了G. Gangsters in Central City相关的知识,希望对你有一定的参考价值。
给出一棵$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+10;
struct rec int pre,to; a[N<<1];
int n,Q,dfn[N],tot,head[N],g[N][25],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[10];
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[0]; g[u][0]=fa,dep[u]=dep[fa]+1; size[u]=0;
bool leaf=1;
for (int i=head[u];i;i=a[i].pre)
int v=a[i].to; if (v==fa) continue;
dfs1(v,u); leaf=0; size[u]+=size[v];
if (leaf) size[u]=1;
void dfs2(int u,int fa,int rt)
bool leaf=1;
for (int i=head[u];i;i=a[i].pre)
int v=a[i].to; if (v==fa) continue;
dfs2(v,u,rt); leaf=0;
if (leaf) zs[u]=rt;
void init()
for (int i=1;i<=21;i++)
for (int j=1;j<=n;j++)
g[j][i]=g[g[j][i-1]][i-1];
int lca(int u,int v)
if (dep[u]<dep[v]) swap(u,v);
for (int i=21;i>=0;i--)
if (dep[g[u][i]]>=dep[v]) u=g[u][i];
if (u==v) return u;
for (int i=21;i>=0;i--)
if (g[u][i]!=g[v][i]) u=g[u][i],v=g[v][i];
return g[u][0];
int main()
freopen("gangsters.in","r",stdin);
freopen("gangsters.out","w",stdout);
scanf("%d%d",&n,&Q);
for (int i=2;i<=n;i++)
int u; scanf("%d",&u);
adde(i,u); adde(u,i);
dfs1(1,0);init();
for (int i=head[1];i;i=a[i].pre) dfs2(a[i].to,1,a[i].to);
int ans1=0,ans2=0,ret=0;
while (Q--)
int d; scanf("%s%d",s,&d);
if (s[0]==‘+‘)
ret++;
if (cnt[zs[d]]==0) ans1++; cnt[zs[d]]++;
int pre=0;
if (st[zs[d]].size()!=0)
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]]==1) 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=0;
if (st[zs[d]].size()!=0)
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 0;
以上是关于G. Gangsters in Central City的主要内容,如果未能解决你的问题,请参考以下文章
2016 NEERC, Northern Subregional Contest G.Gangsters in Central City(LCA)
Codeforces Gym 101142 G Gangsters in Central City (lca+dfs序+树状数组+set)
CF G. Orientation of Edges BFS
African Cocoa Trading Center Opens In Central China
Codeforces Round #502 (in memory of Leopoldo Taravilse, Div. 1 + Div. 2) G. The Tree