Codeforces
就是有q(q<=100000)次询问,每次询问给出m个关键点。你可以删除除了关键点之外的任意节点,问你最少删除多少个节点才能使得所有关键点两两不能互达。
套路虚树搞一下,然后我们考虑树形dp。
我们不妨设f[x]表示任意一个关键点都无法到达子树根的最小花费,g[x]表示允许有一个关键点到达子树根的最小花费。容易知道 g[x]≤f[x] g [ x ] ≤ f [ x ] 。我们需要分情况进行讨论。
对于关键点,无法满足f[x]的情况,我们设成INF。而为了满足g[x],那么任意一个子树都无法到达其根,也不需要再砍断,即:
f[x]=INF f [ x ] = I N F
g[x]=∑f[son] g [ x ] = ∑ f [ s o n ]
注意并不能 g[x]=∑min(f[son],g[son]+1) g [ x ] = ∑ min ( f [ s o n ] , g [ s o n ] + 1 ) ,因为这样无法保证有多少个son可以到达x,那么也就是我们可以枚举是否有一个son改成g[son]+1更优,即满足 f[son]>g[son]+1 f [ s o n ] > g [ s o n ] + 1 ,但是这是不可能的。证明很简单,除了x为关键点和无解的情况,对于g[x]的情况仅需要删除连接其儿子和x的一个点即可得到f[x]状态。那么我们就可以这样写方程。
对于非关键点,f[x]要么就是都不能到,要么就是都能到,删除x,两种情况取最小值。g[x]就是在f[x]的基础上使得有一个儿子可以到,我们选择一个f[son]与g[son]差最大的儿子开放。
f[x]=min(∑f[son],1+∑g[son]) f [ x ] = min ( ∑ f [ s o n ] , 1 + ∑ g [ s o n ] )
g[x]=∑f[son]−max(f[son]−g[son]) g [ x ] = ∑ f [ s o n ] − max ( f [ s o n ] − g [ s o n ] )
需要注意一点就是当f[x]只加了一个INF的时候,g[x]还是有效的状态,因为我们可以选择开放f[son]为INF的儿子开放。
上次世界树写倍增常数老大了,这次点数少就写了rmq,果然快了不少,62ms。。
#include
#include
#include
#define rg register
using namespace std;
const int maxn=100010,INF=0x3f3f3f3f;
struct data{int v,nxt;}edge[maxn<<1];
int n,q,m,p,cnt,top,flag,head[maxn],stk[maxn],is[maxn],a[maxn],deep[maxn];
int f[maxn],g[maxn],dfn[2][maxn],pos[maxn];
struct rmq{
int len,lg[maxn<<1],mn[18][maxn<<1];
inline int getmin(int x,int y){return deep[x]void push(int x){mn[0][++len]=x;}
void init()
{
lg[0]=-1;
for(rg int i=1;i<=len;i++) lg[i]=lg[i>>1]+1;
for(int i=1;i<18;i++)
for(rg int l=1,r=1+(1<1);r<=len;l++,r++)
mn[i][l]=getmin(mn[i-1][l],mn[i-1][r]);
}
int query(int l,int r)
{
int p=lg[r-l+1],k=r-(1<1;
return getmin(mn[p][l],mn[p][k]);
}
int lca(int x,int y)
{
return query(min(dfn[0][x],dfn[0][y]),max(dfn[1][x],dfn[1][y]));
}
int dis(int x,int y){return deep[x]+deep[y]-(deep[lca(x,y)]<<1);}
}ST;
template <typename Tp> inline void read(Tp &x)
{
x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
inline int cmp(int x,int y){return dfn[0][x]0][y];}
inline void insert(int u,int v){edge[++p]=(data){v,head[u]};head[u]=p;}
void dfs(int x,int pre)
{
dfn[0][x]=ST.len+1;deep[x]=deep[pre]+1;
for(int i=head[x];i;i=edge[i].nxt)
if(edge[i].v!=pre)
ST.push(x),dfs(edge[i].v,x);
ST.push(x);dfn[1][x]=ST.len;
}
void input()
{
int x,y;
read(n);ST.len=0;
for(rg int i=1;i1,0);read(q);memset(head,0,sizeof(head));
ST.init();
}
void build()
{
read(m);cnt=top=p=flag=0;
for(int i=1;i<=m;i++) read(a[i]),is[a[i]]=1;
sort(a+1,a+m+1,cmp);
if(!is[1]) stk[++top]=1;
for(int i=1,t;i<=m;i++)
{
t=0;
while(top)
{
t=ST.lca(a[i],stk[top]);
if(top>1&&deep[stk[top-1]]>deep[t])
insert(stk[top-1],stk[top]),top--;
else if(deep[stk[top]]>deep[t])
{insert(t,stk[top]);top--;break;}
else break;
}
if(stk[top]!=t) stk[++top]=t;
stk[++top]=a[i];
}
while(top>1) insert(stk[top-1],stk[top]),top--;
}
void dp(int x)
{
pos[++cnt]=x;
if(is[x])
{
f[x]=INF;
for(int i=head[x];i;i=edge[i].nxt)
{
dp(edge[i].v);
if(f[edge[i].v]>=INF)
{
if(ST.dis(x,edge[i].v)<=1) flag=1;
else g[x]+=1+g[edge[i].v];
}
else g[x]+=f[edge[i].v];
}
}
else
{
int sf=0,sg=0,mx=0,cnt=0;
for(int i=head[x];i;i=edge[i].nxt)
{
dp(edge[i].v);
if(f[edge[i].v]==INF) cnt++;
sg+=g[edge[i].v];
if(cnt<2)
{
sf+=f[edge[i].v];
mx=max(mx,f[edge[i].v]-g[edge[i].v]);
}
else sf=INF,mx=0;
}
f[x]=min(sf,1+sg);g[x]=min(f[x],sf-mx);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
input();
while(q--)
{
build();
dp(1);
if(flag) puts("-1");
else printf("%d\n",g[1]);
for(int i=1;i<=cnt;i++) f[pos[i]]=g[pos[i]]=head[pos[i]]=is[pos[i]]=0;
}
return 0;
}