• CF613D Kingdom and its Cities


    题目

    CF613D Kingdom and its Cities

    给定一棵树,每次给定一些关键点,要求割掉最少的点使得这些点两两之间不连通。

    分析

    虚树。

    首先很明显是个虚树,于是可以直接建虚树,然后就开始 (dp)

    我们可以使用“状态机”这样的模型:用状态的 0/1 来规定当前点选/不选。

    那么这道题就很简单了,直接分类讨论然后直接做即可。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    template <typename T>
    inline void read(T &x){
    	x=0;bool f=false;char ch=getchar();
    	while(!isdigit(ch)){f|=ch=='-';ch=getchar();}
    	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	x=f?-x:x;
    	return ;
    } 
    template <typename T>
    inline void write(T x){
    	if(x<0) putchar('-'),x=-x;
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    	return ;
    }
    #define int long long
    const int N=1e6+5,t=20,INF=1e18+7;
    int n,m;
    int head[N],to[N],nex[N],val[N],idx;
    int dfn[N],Min[N],dp[N][2],q[N],sta[N],k,top,DFN;
    bool vis[N],flag;
    int Top[N],dep[N],siz[N],fa[N],son[N];
    inline void add(int u,int v){
    	nex[++idx]=head[u];
    	to[idx]=v;
    	head[u]=idx;
    	val[idx]=((u==fa[v])||(v==fa[u]));
    	return ;	
    }
    
    void dfs1(int x,int f){
    	siz[x]=1;dep[x]=dep[f]+1;fa[x]=f;
    	for(int i=head[x];i;i=nex[i]){
    		int y=to[i];
    		if(y==f) continue;
    		dfs1(y,x);siz[x]+=siz[y];
    		if(siz[son[x]]<siz[y]) son[x]=y;
    	}
    	return ;
    }
    void dfs2(int x,int f){
    	if(son[fa[x]]==x) Top[x]=Top[f];
    	else Top[x]=x;
    	dfn[x]=++DFN;
    	if(son[x]) dfs2(son[x],x);
    	for(int i=head[x];i;i=nex[i]){
    		int y=to[i];
    		if(y==f||y==son[x]) continue;
    		dfs2(y,x);	
    	} 
    	return ;
    }
    int QueryLca(int x,int y){
    	while(Top[x]!=Top[y]){
    		if(dep[Top[x]]<dep[Top[y]]) swap(x,y);
    		x=fa[Top[x]];
    	}
    	return dep[x]<dep[y]?x:y;
    }
    inline bool Cmp(const int &x,const int &y){return dfn[x]<dfn[y];}
    void DP(int x,int f){
    	dp[x][0]=(vis[x]^1)*n,dp[x][1]=vis[x]*n;
    	int num=0,rec=0,Max=0;
    	for(int i=head[x];i;i=nex[i]){
    		int y=to[i];
    		if(y==f) continue;
    		num+=vis[y],DP(y,x);
    		if(vis[x]){
    			if(vis[y]&&val[i]) flag=1;	
    			dp[x][0]+=min(dp[y][1],dp[y][0]+1);
    		}
    		else{
    			Max=max(Max,dp[y][1]-dp[y][0]);
    			rec+=min(dp[y][0],dp[y][1]);
    			dp[x][1]+=dp[y][1];
    		}
    	}
    	if(!vis[x]){
    		if(num<=1) dp[x][0]=dp[x][1]-Max;
    		dp[x][1]=min(dp[x][1],rec+1);
    	}
    	return ;
    }
    int clear[N],Cnt;
    void BuildVTree(){
    	sort(q+1,q+k+1,Cmp);
    	top=0,sta[++top]=1;head[1]=0;idx=0;
    	for(int i=1;i<=k;i++){
    		const int x=q[i];
    		if(x==1) continue;head[x]=0;
    		if(top<2){sta[++top]=x;continue;}
    		const int lca=QueryLca(sta[top],x);
    		if(sta[top]==lca){sta[++top]=x;continue;}
    		while(top>1&&dfn[sta[top-1]]>=dfn[lca]) add(sta[top-1],sta[top]),top--;
    		if(sta[top]!=lca) head[lca]=0,add(lca,sta[top]),sta[top]=lca;
    		sta[++top]=x;
    	}
    	while(top>1) add(sta[top-1],sta[top]),top--;
    	return ;
    }
    signed main(){
    	read(n);
    	for(int i=1;i<n;i++){
    		int u,v,w;
    		read(u),read(v);
    		add(u,v),add(v,u);
    	}
    	read(m);
    	dfs1(1,0);dfs2(1,0);
    	memset(head,0,sizeof(head)),idx=0;
    	while(m--){
    		read(k);flag=false;
    		for(int i=1;i<=k;i++) read(q[i]),vis[q[i]]=true;
    		BuildVTree();
    		DP(1,0);
    		write(flag?-1:min(dp[1][0],dp[1][1])),putchar('
    ');
    		for(int i=1;i<=k;i++) vis[q[i]]=false;
    	}
    	return 0;
    }
    

    这道题要注意的就是虚树的建法(准确的来说是每次重置的办法,用 (vector) 就没个问题。)

    还有就是建虚树时那个 (while) 里面的判断条件是 (ge) 不是 (le) 。。

  • 相关阅读:
    glog入门demo
    gflag的简单入门demo
    caffe库源码剖析——net层
    排序算法的c++实现——计数排序
    docker的/var/lib/docker目录迁移
    SpringCloud Ribbon 负载均衡 通过服务器名无法连接的神坑一个
    Spring Boot Cache使用与整合
    Navicat Keygen
    Windows / Office
    docker swarm 搭建与服务更新
  • 原文地址:https://www.cnblogs.com/Akmaey/p/14737462.html
Copyright © 2020-2023  润新知