【题解】CodeForces-613D Kingdom and its Cities

话说之前自己都不相信自己能一遍打对,交都没交就傻傻地对拍了,所以文末附赠数据生成器

Problem

CodeForces

未经润色的题目概要:
给定一棵有 n n 个节点的树,q次询问,每次询问给 k k 个点,求至少删除多少点,使得这k个点两两不属于同一联通块( n,q,k100000 n , q , ∑ k ≤ 100000

Solution

根据这类型的数据限制: k100000 ∑ k ≤ 100000

明显想到套路建虚树

然后根据题意设
f[x] f [ x ] 为以 x x 为根的子树中的关键点满足上述条件且这些关键点不能到达x的最少删除节点数,个人喜欢叫做完全封锁
g[x] g [ x ] 为以 x x 为根的子树中的关键点满足上述条件且这些关键点可能到达x的最少删除节点数,个人喜欢叫做不完全封锁

有一定的树型Dp基础就能推出:

x x 为关键点时:

  • f[x]=+

    • g[x]=f[son] g [ x ] = ∑ f [ s o n ]

    • x x 不是关键节点时:

      • f[x]=min(f[son],1+g[son])

      • g[x]=min(f[x],f[son]max{f[son]g[son]}) g [ x ] = min ( f [ x ] , ∑ f [ s o n ] − max { f [ s o n ] − g [ s o n ] } )

      • 解释:

        • x x 为关键点时,x节点不可删除,所以至少有一个点到达 x x ,所以f[x]不成立
        • 由于 x x 已经为关键点,所以不能让任何一个x的子树影响到 x x ,所以所有儿子应该取f
        • x x 不是关键节点时,为了使没有关键点能到x,可以封锁自己(这时 f[x] f [ x ] 是封锁自己的1,加上所有儿子不完全封锁 g[son] ∑ g [ s o n ] ),也可以在每个儿子那解决(对每个儿子完全封锁 f[son] ∑ f [ s o n ]
        • x x 不是关键节点时,任何完全封锁的方案可以应用到这(f[x]),或者在儿子中取一个不完全封锁,其他完全封锁(如果有两个不完全封锁,则他们肯定会在 x x 处相遇,则不符合题意)(为了方便计算,描述等价于f[son]max{f[son]g[son]}

        另外要注意

        • 如果虚树上两个节点( x,son x , s o n )之间有省略的,则在考虑 x x 的时候f[son]=min(f[son],1+g[son]),这是因为可以借助中间省略的节点使得这个不完全封锁变成完全封锁
        • 由于其中有 + + ∞ 的计算,所以要特判它的存在
        • RMQ R M Q LCA L C A 真的很块QwQ

        所以这题考的是树型Dp基础,虚树只是一个幌子

        Code

        数据生成器在下面

        代码:

        /*
        st[x]表示把这个子树全部切断,不会影响父亲
        g[x]表示把这个子树切断,可能会影响父亲
        
        is:
            f[x]=+oo
        not:
            f[x]= min ( ∑f[son] , 1 + ∑g[son] )
        
        
        not:
            g[x]= min ( f[x] , ∑f[son] - max{ f[son]-g[son] } )
        is:
            g[x]=  ∑f[son]
        */
        #include<bits/stdc++.h>
        using namespace std;
        #define rg register
        
        template <typename _Tp> inline _Tp read(_Tp&x){
            char c11=getchar(),ob=0;x=0;
            while(c11^'-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1;
            while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return x;
        }
        
        const int N=201000,M=20,inf=0x3f3f3f3f;
        struct Edge{int v,nxt;}a[N<<1];
        int head[N],depth[N],is[N],f[N],g[N],sta[N],b[N],p[N],dfn[N];
        int n,Q,m,s,lct,_,tot,top,dfc;
        
        struct LCA{
            int in[N],out[N],blist[N],Log[N];
            int st[N][M];
        
            void dfs(int x,int fa){
                dfn[x]=++dfc;
                depth[x]=depth[fa]+1;
                in[x]=++lct;blist[lct]=x;
                for(int i=head[x];i;i=a[i].nxt)
                    if(a[i].v!=fa){
                        dfs(a[i].v,x);
                        blist[++lct]=x;
                    }
                out[x]=lct;
                return ;
            }
        
            void pre(){
                dfs(1,0);
                Log[0]=-1;
                for(rg int i=1;i<=lct;++i)
                    st[i][0]=blist[i],Log[i]=Log[i>>1]+1;
                for(rg int l=1,len=1;len<=lct;++l,len<<=1)
                for(rg int i=1;i+len-1<=lct;++i)
                    st[i][l]=depth[st[i][l-1]]<depth[st[i+len][l-1]]?st[i][l-1]:st[i+len][l-1];
                return ;
            }
        
            inline int lca(int x,int y){
                if(in[x]<in[y]&&out[y]<out[x])return x;
                if(in[x]>in[y]&&out[y]>out[x])return y;
                if(in[x]>out[y])swap(x,y);
                int l=out[x],r=in[y],len=r-l+1;
                int t1=st[l][Log[len]],t2=st[r-(1<<Log[len])+1][Log[len]];
                return depth[t1]<depth[t2]?t1:t2;
            }
        }t;
        
        inline int cmp(const int&AA,const int&BB){return dfn[AA]<dfn[BB];}
        
        inline void add(int u,int v){a[++_].v=v,a[_].nxt=head[u],head[u]=_;}
        
        inline void dp(int x){
            p[++tot]=x;
            if(is[x]){
                f[x]=inf;g[x]=0;
                for(int i=head[x];i;i=a[i].nxt){
                    dp(a[i].v);
                    if(depth[a[i].v]>depth[x]+1)f[a[i].v]=min(f[a[i].v],g[a[i].v]+1);
                    if(f[a[i].v]!=inf&&g[x]!=inf)
                        g[x]+=f[a[i].v];
                    else g[x]=inf;
                }
            }else {
                int sf=0,sg=0,mx=0,tcnt=0;
                for(int i=head[x];i;i=a[i].nxt){
                    dp(a[i].v);
                    if(depth[a[i].v]>depth[x]+1)f[a[i].v]=min(f[a[i].v],g[a[i].v]+1);
                    if(f[a[i].v]==inf)++tcnt;
                    if(sf!=inf&&f[a[i].v]!=inf)
                        sf+=f[a[i].v];
                    else sf=inf;
                    if(sg!=inf&&g[a[i].v]!=inf)
                        sg+=g[a[i].v];
                    else sg=inf;
                    if(f[a[i].v]!=inf&&g[a[i].v]!=inf)
                    mx=max(mx,f[a[i].v]-g[a[i].v]);
                }
                g[x]=f[x]=min(sf,1+sg);
                if(tcnt>1)return ;
                if(tcnt==0){g[x]=min(g[x],sf-mx);return ;}
                sf=0;
                for(rg int i=head[x];i;i=a[i].nxt)
                    if(f[a[i].v]!=inf)
                        sf+=f[a[i].v];
                    else sf+=g[a[i].v];
                g[x]=min(g[x],sf);
            }return ;
        }
        
        int main(){
            read(n);
            for(rg int i=1,x,y;i<n;++i)read(x),read(y),add(x,y),add(y,x);
            t.pre();read(Q);
            for(rg int i=1;i<=n;++i)head[i]=0;_=0;
            while(Q--){
                _=top=tot=0;
                read(m);
                for(rg int i=1;i<=m;++i)is[read(b[i])]=1;
                sort(b+1,b+m+1,cmp);
                if(!is[1])sta[top=1]=1;
                for(rg int i=1;i<=m;++i){
                    int x=b[i],o=0;
                    while(top){
                        o=t.lca(x,sta[top]);
                        if(top>1&&depth[sta[top-1]]>depth[o])
                            add(sta[top-1],sta[top]),--top;
                        else if(depth[o]<depth[sta[top]])
                            {add(o,sta[top]);--top;break;}
                        else break;
                    }
                    if(sta[top]!=o)sta[++top]=o;
                    sta[++top]=x;
                }
                while(top>1)add(sta[top-1],sta[top]),--top;
                dp(1);
                int ans=min(f[1],g[1]);
                if(ans==inf)
                    puts("-1");
                else 
                    printf("%d\n",min(f[1],g[1]));
                for(rg int i=1;i<=tot;++i)is[p[i]]=head[p[i]]=0;
            }return 0;
        }

        数据生成器:

        #include<bits/stdc++.h>
        using namespace std;
        #define rg register
        
        const int N=101000;
        int er[N],d[N];
        
        const int n=1000,m=1000;
        
        inline int f(int x){return d[x]?d[x]=f(d[x]):x;}
        
        int main(){
            freopen("in","w",stdout);
            srand(time(0));
            printf("%d\n",n);
            for(rg int i=1;i<n;++i){
                int x,y;
                do{x=rand()%n+1,y=rand()%n+1;}while(f(x)==f(y));
                printf("%d %d\n",x,y);
                d[f(x)]=y;
            }
            for(rg int i=1;i<=n;++i)er[i]=i;
            printf("%d\n",m);
            for(rg int c=1;c<=m;++c){
                int t=rand()%n+1;
                random_shuffle(er+1,er+n+1);
                printf("%d",t);
                for(rg int i=1;i<=t;++i)printf(" %d",er[i]);
                putchar('\n');
            }return 0;
        }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值