P5157 [USACO18DEC] The Cow Gathering P 题解
P5157 [USACO18DEC] The Cow Gathering P 题解
一种比正解复杂的但是比较好想的做法。
我们先简化一下题意,对于每一个限制对 \((a,b)\),我们令 \(b\) 为根结点,那么 \(a\) 的子树内的点都无法最后被删去,(因为 \(a\) 无法最后被删去,删了 \(a\) 就断了)。
我们将这些点与 \(b\) 暴力连边,那么出度为 \(0\) 的点就可以最后选。
当然,还要判断无解情况:如果这些边构成了一个环,那么本身无论如何都删不完,所以所有点都无解。
因此我们有了 \(O(n^2)\) 的暴力做法,可以得到 20 分。
考虑优化,发现换根的过程增大了不必要的时间开销,尝试省掉这个过程。
我们钦定 \(1\) 为根结点,那么对于限制对 \((a,b)\),有如下两种情况:
- \(b\) 在 \(a\) 的子树之外。
这种情况下,换成以 \(b\) 为根的时候,\(a\) 的子树是不变的。
- \(b\) 在 \(a\) 的子树之内。
这种情况子树就会变化。

如这一张图,我们换到 \(b\) 为根时,\(a\) 的子树包含的点是 \(\{a,4,9,5,2,1,3\}\)。也就是说,我们要把 \(a\) 的子树中 \(b\) 所在的子树删掉。如果我们对原树做一个 dfn 序,新的子树会被分成两连续部分。
考虑寻找 \(b\) 所在子树对应的 \(a\) 的儿子 \(v\),我们相当于要把 \(v\) 所在的子树删去。
这个寻找的过程显然可以用树链剖分进行维护,我们从 \(b\) 开始暴力跳链,如果 \(a\) 和 \(b\) 链顶不一样就跳到链顶,如果已经满足 \(fa[b]=a\) 就结束,否则上跳到下一条链继续。
若已经跳到在同一个重链上了,那么 \(v\) 就是 \(son[u]\)。
我们对于一段 dfn 连续的区间的出度 \(+1\),可以用差分实现,但是还要找到环判断无解,因此还是需要线段树优化建图,跑一个 tarjan 找环。
时间复杂度 \(O(n \log n)\)。
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int f[500010],dep[500010],sz[500010],dfn[500010],son[500010],top[500010],idx;
int oedge[500010];
vector<int> nG[2000010],g[500010];
void dfs(int u,int fa){
f[u]=fa,dep[u]=dep[fa]+1,sz[u]=1;
for(auto v:g[u]){
if(v^fa){
dfs(v,u);
son[u]=sz[v]>sz[son[u]]?v:son[u];
sz[u]+=sz[v];
}
}
}
void dfs2(int u,int topnode){
top[u]=topnode,dfn[u]=++idx;
if(son[u]) dfs2(son[u],topnode);
for(auto v:g[u]){
if((v^son[u])&&(v^f[u])) dfs2(v,v);
}
}
int dfn_tar[2000010],n_tar,scc,low[2000010],idx_tar,gid[2000010],fid[500010],n,m,ons[2000010];
int st[2000010],top_tar;
void build(int id,int l,int r){
gid[id]=++n_tar;
if(l==r){
fid[l]=gid[id];
return;
}
int mid=(l+r)>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
nG[gid[id<<1]].push_back(gid[id]);
nG[gid[id<<1|1]].push_back(gid[id]);
}
void update(int id,int l,int r,int ql,int qr,int v){
if(ql<=l&&r<=qr){
nG[gid[id]].push_back(fid[v]);
return;
}
int mid=(l+r)>>1;
if(ql<=mid) update(id<<1,l,mid,ql,qr,v);
if(qr>mid) update(id<<1|1,mid+1,r,ql,qr,v);
}
void tarjan(int u){
low[u]=dfn_tar[u]=++idx_tar;
ons[u]=1;
st[++top_tar]=u;
for(auto v:nG[u]){
if(!dfn_tar[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}else if(ons[v]) low[u]=min(low[u],dfn_tar[v]);
}
if(low[u]==dfn_tar[u]){
scc++;
while(st[top_tar]!=u){
ons[st[top_tar]]=0;
top_tar--;
}
ons[u]=0;
top_tar--;
}
}
int main(){
n=read(),m=read();
for(int i=2;i<=n;i++){
int u=read(),v=read();
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1,0);
dfs2(1,1);
build(1,1,n);
for(int i=1;i<=m;i++){
int u=read(),v=read();
int vp=v;
if(dfn[v]<dfn[u]||dfn[v]>dfn[u]+sz[u]-1){
update(1,1,n,dfn[u],dfn[u]+sz[u]-1,dfn[vp]);
oedge[dfn[u]]++,oedge[dfn[u]+sz[u]]--;
continue;
}
while(top[v]!=top[u]){
v=top[v];
if(f[v]==u) break;
v=f[v];
}
if(f[v]!=u) v=son[u];
if(dfn[v]>=2){
update(1,1,n,1,dfn[v]-1,dfn[vp]);
oedge[1]++,oedge[dfn[v]]--;
}
if(dfn[v]+sz[v]<=n){
update(1,1,n,dfn[v]+sz[v],n,dfn[vp]);
oedge[dfn[v]+sz[v]]++;
}
}
for(int i=1;i<=n_tar;i++){
if(!dfn_tar[i]) tarjan(i);
}
if(scc!=n_tar){
for(int i=1;i<=n;i++) printf("0\n");
return 0;
}
for(int i=2;i<=n;i++){
oedge[i]+=oedge[i-1];
}
for(int i=1;i<=n;i++){
printf("%d\n",oedge[dfn[i]]==0);
}
return 0;
}

浙公网安备 33010602011771号