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('\n');
		for(int i=1;i<=k;i++) vis[q[i]]=false;
	}
	return 0;
}

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

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

posted @ 2021-05-07 00:02  __Anchor  阅读(40)  评论(0编辑  收藏  举报