【CF613D】Kingdom and its Cities
题意:给你一棵树,每次询问给出k个关键点,问做多干掉多少个非关键点才能使得所有关键点两两不连通。
$n,\sum k\le 10^5$
题解:刷虚树板子啦!
首先如果两个关键点相邻则无解。然后建出虚树,进行树形DP。设f[i]表示i子树中的关键点都不连通,且i子树中的点与外面的点也不连通的最小花费,g[i]表示i子树重的关键点都不连通,且子树中只有一个点与外面的点连通的最小花费。转移时讨论一波即可。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <vector> using namespace std; const int maxn=100010; int n,m,now,cnt,top; int to[maxn<<1],nxt[maxn<<1],head[maxn],fa[19][maxn],Log[maxn],dep[maxn],A[maxn],vis[maxn],st[maxn],f[maxn],g[maxn],p[maxn],q[maxn]; vector<int> ch[maxn]; void dfs(int x) { p[x]=++q[0],q[q[0]]=x; for(int i=head[x];i!=-1;i=nxt[i]) if(to[i]!=fa[0][x]) fa[0][to[i]]=x,dep[to[i]]=dep[x]+1,dfs(to[i]); } inline void add(int a,int b) { to[cnt]=b,nxt[cnt]=head[a],head[a]=cnt++; } bool cmp(const int &a,const int &b) { return p[a]<p[b]; } inline int lca(int a,int b) { if(dep[a]<dep[b]) swap(a,b); for(int i=Log[dep[a]-dep[b]];i>=0;i--) if(dep[fa[i][a]]>=dep[b]) a=fa[i][a]; if(a==b) return a; for(int i=Log[dep[a]];i>=0;i--) if(fa[i][a]!=fa[i][b]) a=fa[i][a],b=fa[i][b]; return fa[0][a]; } void DP(int x) { int y,t0=0,t1=0,t2=0; vector<int>::iterator it; for(it=ch[x].begin();it!=ch[x].end();it++) { y=*it,DP(y); t2=min(t2+f[y],t0+min(f[y],g[y])),t0+=f[y],t1+=min(f[y],g[y]); } ch[x].clear(); if(vis[x]==now) { g[x]=t0,f[x]=t0+1; } else { g[x]=t2,f[x]=min(t1+1,t0); } } inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘) f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+(gc^‘0‘),gc=getchar(); return ret*f; } int main() { //freopen("a.in","r",stdin); n=rd(); int i,j,a,b,c; memset(head,-1,sizeof(head)); for(i=2;i<=n;i++) a=rd(),b=rd(),add(a,b),add(b,a),Log[i]=Log[i>>1]+1; dep[1]=1,dfs(1); for(j=1;(1<<j)<=n;j++) for(i=1;i<=n;i++) fa[j][i]=fa[j-1][fa[j-1][i]]; m=rd(); for(now=1;now<=m;now++) { a=rd(); for(i=1;i<=a;i++) A[i]=rd(),vis[A[i]]=now; for(i=1;i<=a;i++) if(vis[fa[0][A[i]]]==now) { puts("-1"); break; } if(i<=a) continue; sort(A+1,A+a+1,cmp); st[top=1]=A[1]; for(i=2;i<=a;i++) { c=lca(A[i-1],A[i]); while(top&&dep[st[top]]>dep[c]) { b=st[top--]; if(top&&dep[st[top]]>dep[c]) ch[st[top]].push_back(b); else ch[c].push_back(b); } if(!top||st[top]!=c) st[++top]=c; st[++top]=A[i]; } while(top>1) ch[st[top-1]].push_back(st[top]),top--; DP(st[top]); a=min(g[st[top]],f[st[top]]); printf("%d\n",a); } return 0; }