话说之前自己都不相信自己能一遍打对,交都没交就傻傻地对拍了,所以文末附赠数据生成器
Problem
未经润色的题目概要:
给定一棵有
n
n
个节点的树,次询问,每次询问给
k
k
个点,求至少删除多少点,使得这个点两两不属于同一联通块(
n,q,∑k≤100000
n
,
q
,
∑
k
≤
100000
)
Solution
根据这类型的数据限制: ∑k≤100000 ∑ k ≤ 100000
明显想到套路建虚树
然后根据题意设
f[x]
f
[
x
]
为以
x
x
为根的子树中的关键点满足上述条件且这些关键点不能到达的最少删除节点数,个人喜欢叫做完全封锁
g[x]
g
[
x
]
为以
x
x
为根的子树中的关键点满足上述条件且这些关键点可能到达的最少删除节点数,个人喜欢叫做不完全封锁
有一定的树型Dp基础就能推出:
当 x x 为关键点时:
g[x]=∑f[son] g [ x ] = ∑ f [ s o n ]
当 x x 不是关键节点时:
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 x 已经为关键点,所以不能让任何一个的子树影响到 x x ,所以所有儿子应该取
- 当 x x 不是关键节点时,为了使没有关键点能到,可以封锁自己(这时 f[x] f [ x ] 是封锁自己的1,加上所有儿子不完全封锁 ∑g[son] ∑ g [ s o n ] ),也可以在每个儿子那解决(对每个儿子完全封锁 ∑f[son] ∑ f [ s o n ] )
- 当 x x 不是关键节点时,任何完全封锁的方案可以应用到这(),或者在儿子中取一个不完全封锁,其他完全封锁(如果有两个不完全封锁,则他们肯定会在 x x 处相遇,则不符合题意)(为了方便计算,描述等价于)
另外要注意
- 如果虚树上两个节点( x,son x , s o n )之间有省略的,则在考虑 x x 的时候,这是因为可以借助中间省略的节点使得这个不完全封锁变成完全封锁
- 由于其中有 +∞ + ∞ 的计算,所以要特判它的存在
- 用 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; }