Codeforces 613D(虚树)

题目大意

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

分析

虚树上贪心/DP即可。

虚树

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

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

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

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

好了虚树就构建好了。

说几个注意的点:

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

代码

#include
#include
#include
#include
#include
#include
#include
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]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;iscanf("%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]void Build()
{
    sort(d+1,d+cnt+1,cmp);
    int to=cnt;
    for(int i=1;i1]);
    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]]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;
}

你可能感兴趣的:(Codeforces,虚树)