Codeforces 613D(虚树)

xiaoxiao2021-02-28  40

题目大意

链接 给定一棵树,每次给定一些点,求最少要阻断多少点使得这些点之间两两之间无法通行。其中不能阻断给定点,无解输出-1.

分析

虚树上贪心/DP即可。

虚树

由于在树上有可能有很多点根本就不需要用,所以我们要把这些点提出来单独组成一棵树,减少了大量运算。

一般来说我们要提出的点是所有的点和这些点之间两两LCA的点,然而K个点LCA的点仅仅有K-1个,也就是按照DFS序排序之后相邻的点之间LCA即可。

我挑了一种比较好理解和易于自己实现的写法。 首先获得原树,然后遍历之后得到DFS序,并且拥有一个办法求LCA,求出DFS序,求出每个点所在子树的时间戳范围。 对于每一组数据我们有如下操作:

获取结点,按照DFS序排序,相邻结点之间获取它们的LCA,再把所有结点按照DFS序排序,去重。弄一个栈,先加入DFS序最小的,它也就是整个虚树的根。依次加入结点,如果当前结点不在栈顶结点的子树内,就弹出栈顶结点。然后连接栈顶元素和当前元素,把当前元素入栈。

好了虚树就构建好了。

说几个注意的点:

建立虚树的时候是个有根树,可以不向根的方向连边,因此遍历的时候不用判断父亲结点。边和其它数据的还原最好利用我们得到的结点逐个还原,因为原树太大了。注意排序的时候要加cmp函数,但是unique的时候不要加。

代码

#include<cmath> #include<queue> #include<cctype> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int maxn=3e5+105,inf=1e6,oo=18; int n,m; int dfn,l[maxn],r[maxn],dep[maxn],fa[maxn][oo+5]; struct Tree1 { int np,first[maxn]; struct edge{ int to,next; }E[maxn<<1]; void add(int u,int v) { E[++np]=(edge){v,first[u]}; first[u]=np; } void DFS(int i,int f,int d) { l[i]=++dfn; dep[i]=d; fa[i][0]=f; for(int j=1;j<=oo;j++)fa[i][j]=fa[fa[i][j-1]][j-1]; for(int p=first[i];p;p=E[p].next) { int j=E[p].to; if(j==f)continue; DFS(j,i,d+1); } r[i]=dfn; } int LCA(int u,int v) { if(dep[u]<dep[v])swap(u,v); int delt=(dep[u]-dep[v]); for(int j=0;j<=oo;j++) if((delt>>j)&1)u=fa[u][j]; if(u==v)return u; for(int j=oo;j>=0;j--) if(fa[u][j]!=fa[v][j])u=fa[u][j],v=fa[v][j]; return fa[u][0]; } void Init() { int u,v; scanf("%d",&n); for(int i=1;i<n;i++) { scanf("%d%d",&u,&v); add(u,v);add(v,u); } DFS(1,0,1); scanf("%d\n",&m); } }tr; int cnt,np,first[maxn],d[maxn],stk[maxn],top; bool vis[maxn]; int sz[maxn],f[maxn]; struct edge{ int to,next; }E[maxn<<1]; void add(int u,int v) { E[++np]=(edge){v,first[u]}; first[u]=np; } bool cmp(int x,int y) { return l[x]<l[y]; } void Build() { sort(d+1,d+cnt+1,cmp); int to=cnt; for(int i=1;i<to;i++) d[++cnt]=tr.LCA(d[i],d[i+1]); sort(d+1,d+cnt+1,cmp); cnt=unique(d+1,d+cnt+1)-d-1; top=0; stk[++top]=d[1]; for(int i=2;i<=cnt;i++) { while(top && r[stk[top]]<l[d[i]])top--; if(top) add(stk[top],d[i]); stk[++top]=d[i]; } } void dp(int i) { f[i]=0; if(vis[i]) { sz[i]=1; for(int p=first[i];p;p=E[p].next) { int j=E[p].to; dp(j); f[i]+=f[j]; if(sz[j])f[i]++; } } else { sz[i]=0; for(int p=first[i];p;p=E[p].next) { int j=E[p].to; dp(j); f[i]+=f[j]; sz[i]+=sz[j]; } if(sz[i]>1) { sz[i]=0; f[i]++; } } } void query() { bool ok=false; scanf("%d",&cnt); for(int i=1;i<=cnt;i++) { scanf("%d",&d[i]); vis[d[i]]=1; } for(int i=1;i<=cnt;i++) if(vis[fa[d[i]][0]])ok=true; if(ok) printf("-1\n"); else { Build(); dp(d[1]); printf("%d\n",f[d[1]]); } np=0; for(int i=1;i<=cnt;i++) { vis[d[i]]=0; first[d[i]]=0; } } int main() { tr.Init(); while(m--) query(); return 0; }
转载请注明原文地址: https://www.6miu.com/read-2630165.html

最新回复(0)