Codeforces613D Kingdom and its Cities

Problem

Codeforces
就是有q(q<=100000)次询问,每次询问给出m个关键点。你可以删除除了关键点之外的任意节点,问你最少删除多少个节点才能使得所有关键点两两不能互达。

Solution

套路虚树搞一下,然后我们考虑树形dp。
我们不妨设f[x]表示任意一个关键点都无法到达子树根的最小花费,g[x]表示允许有一个关键点到达子树根的最小花费。容易知道 g[x]f[x] g [ x ] ≤ f [ x ] 。我们需要分情况进行讨论。

对于关键点,无法满足f[x]的情况,我们设成INF。而为了满足g[x],那么任意一个子树都无法到达其根,也不需要再砍断,即:
f[x]=INF f [ x ] = I N F
g[x]=f[son] g [ x ] = ∑ f [ s o n ]
注意并不能 g[x]=min(f[son],g[son]+1) g [ x ] = ∑ min ( f [ s o n ] , g [ s o n ] + 1 ) ,因为这样无法保证有多少个son可以到达x,那么也就是我们可以枚举是否有一个son改成g[son]+1更优,即满足 f[son]>g[son]+1 f [ s o n ] > g [ s o n ] + 1 ,但是这是不可能的。证明很简单,除了x为关键点和无解的情况,对于g[x]的情况仅需要删除连接其儿子和x的一个点即可得到f[x]状态。那么我们就可以这样写方程。

对于非关键点,f[x]要么就是都不能到,要么就是都能到,删除x,两种情况取最小值。g[x]就是在f[x]的基础上使得有一个儿子可以到,我们选择一个f[son]与g[son]差最大的儿子开放。
f[x]=min(f[son],1+g[son]) f [ x ] = min ( ∑ f [ s o n ] , 1 + ∑ g [ s o n ] )
g[x]=f[son]max(f[son]g[son]) g [ x ] = ∑ f [ s o n ] − max ( f [ s o n ] − g [ s o n ] )

需要注意一点就是当f[x]只加了一个INF的时候,g[x]还是有效的状态,因为我们可以选择开放f[son]为INF的儿子开放。

上次世界树写倍增常数老大了,这次点数少就写了rmq,果然快了不少,62ms。。

Code

#include 
#include 
#include 
#define rg register
using namespace std;
const int maxn=100010,INF=0x3f3f3f3f;
struct data{int v,nxt;}edge[maxn<<1];
int n,q,m,p,cnt,top,flag,head[maxn],stk[maxn],is[maxn],a[maxn],deep[maxn];
int f[maxn],g[maxn],dfn[2][maxn],pos[maxn];
struct rmq{
    int len,lg[maxn<<1],mn[18][maxn<<1];
    inline int getmin(int x,int y){return deep[x]void push(int x){mn[0][++len]=x;}
    void init()
    {
        lg[0]=-1;
        for(rg int i=1;i<=len;i++) lg[i]=lg[i>>1]+1;
        for(int i=1;i<18;i++)
          for(rg int l=1,r=1+(1<1);r<=len;l++,r++)
            mn[i][l]=getmin(mn[i-1][l],mn[i-1][r]);
    }
    int query(int l,int r)
    {
        int p=lg[r-l+1],k=r-(1<1;
        return getmin(mn[p][l],mn[p][k]);
    }
    int lca(int x,int y)
    {
        return query(min(dfn[0][x],dfn[0][y]),max(dfn[1][x],dfn[1][y]));
    }
    int dis(int x,int y){return deep[x]+deep[y]-(deep[lca(x,y)]<<1);}
}ST;
template <typename Tp> inline void read(Tp &x)
{
    x=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
inline int cmp(int x,int y){return dfn[0][x]0][y];}
inline void insert(int u,int v){edge[++p]=(data){v,head[u]};head[u]=p;}
void dfs(int x,int pre)
{
    dfn[0][x]=ST.len+1;deep[x]=deep[pre]+1;
    for(int i=head[x];i;i=edge[i].nxt)
      if(edge[i].v!=pre)
        ST.push(x),dfs(edge[i].v,x);
    ST.push(x);dfn[1][x]=ST.len;
}
void input()
{
    int x,y;
    read(n);ST.len=0;
    for(rg int i=1;i1,0);read(q);memset(head,0,sizeof(head));
    ST.init();
}
void build()
{
    read(m);cnt=top=p=flag=0;
    for(int i=1;i<=m;i++) read(a[i]),is[a[i]]=1;
    sort(a+1,a+m+1,cmp);
    if(!is[1]) stk[++top]=1;
    for(int i=1,t;i<=m;i++)
    {
        t=0;
        while(top)
        {
            t=ST.lca(a[i],stk[top]);
            if(top>1&&deep[stk[top-1]]>deep[t])
              insert(stk[top-1],stk[top]),top--;
            else if(deep[stk[top]]>deep[t])
              {insert(t,stk[top]);top--;break;}
            else break;
        }
        if(stk[top]!=t) stk[++top]=t;
        stk[++top]=a[i];
    }
    while(top>1) insert(stk[top-1],stk[top]),top--;
}
void dp(int x)
{
    pos[++cnt]=x;
    if(is[x])
    {
        f[x]=INF;
        for(int i=head[x];i;i=edge[i].nxt)
        {
            dp(edge[i].v);
            if(f[edge[i].v]>=INF)
            {
                if(ST.dis(x,edge[i].v)<=1) flag=1;
                else g[x]+=1+g[edge[i].v];
            }
            else g[x]+=f[edge[i].v];
        }
    }
    else
    {
        int sf=0,sg=0,mx=0,cnt=0;
        for(int i=head[x];i;i=edge[i].nxt)
        {
            dp(edge[i].v);
            if(f[edge[i].v]==INF) cnt++;
            sg+=g[edge[i].v];
            if(cnt<2)
            {
                sf+=f[edge[i].v];
                mx=max(mx,f[edge[i].v]-g[edge[i].v]);
            }
            else sf=INF,mx=0;
        }
        f[x]=min(sf,1+sg);g[x]=min(f[x],sf-mx);
    }
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    input();
    while(q--)
    {
        build();
        dp(1);
        if(flag) puts("-1");
        else printf("%d\n",g[1]);
        for(int i=1;i<=cnt;i++) f[pos[i]]=g[pos[i]]=head[pos[i]]=is[pos[i]]=0;
    }
    return 0;
}

你可能感兴趣的:(树形动规,=====动态规划=====,好题集,CodeForce,虚树)