NOI2024 树形图 graphee

· · 题解

D2T3 树形图

首先判掉一些 case:先 tarjan 一遍求出强连通分量,找到一个能到达所有点的强连通分量(也可能找不到),那么 1,2 类点只可能在这个强连通分量中。

若其中没有 1 类点,则强连通分量内所有点都为 2 类点。

### 求出 $1$ 类点 以任意一个 $1$ 类点定为根,求出一棵 dfs 树。考虑在这棵 dfs 树的基础上求出所有 $1$ 类点: 考虑 $fa_u\to u$ 这条边被几条返祖边覆盖了,由于这是一个强连通分量,所以子树中至少有一条返祖边覆盖 $fa_u\to u$。 若有 $\ge 2$ 条返祖边覆盖了 $fa_u\to u$,则 $u\to fa_u$ 有至少两种方案能走到,$u$ 必然不为 $1$ 类点。 若只有 $1$ 条返祖边覆盖了 $fa_u\to u$,设这条边为 $x\to y$,则 $u$ 子树中的点向上走必然要走到 $y$,$u$ 是否为 $1$ 类点等价于 $y$ 是否为 $1$ 类点。于是可以从上到下递推出所有 $1$ 类点。 ### 求出 $2$ 类点 仍然在这棵 dfs 树的基础上求出 $2$ 类点。 如果一个 $u$ 为 $1$ 类点,那么一定有恰好一条返祖边 $x\to y$ 覆盖了 $fa_u\to u$,并且这条返祖边**不能删除**,否则 $x$ 的子树就会脱离这个强连通分量,从而不再是 $1$ 类点。 于是我们得到了若干条返祖边不能删除的限制。 假设我们想判定 $u$,也就是删去若干条边把 $u$ 变为 $1$ 类点,则我们想删去覆盖了 $fa_u\to u$ 的若干条返祖边,使得只剩下一条 $(x,y)$ 覆盖了 $fa_u\to u$,并且 $y$ 也是 $1/2$ 类点,那么 $u$ 就可以成为 $2$ 类点了。 对于一个点 $u$,仍然考虑 $u\to fa_u$ 这条边: 若被两条不能删除的返祖边覆盖,则 $u$ 一定不可行。 若被一条不能删除的返祖边覆盖:设这条边为 $x\to y$,则 $u$ 是否为 $2$ 类点等价于 $y$ 是否为 $1/2$ 类点。 若被零条不能删除的返祖边覆盖:那么 $u$ 子树中有若干条**能删除**的返祖边。若能找到一条能删除的返祖边 $x\to y$,使得 $y$ 为 $1/2$ 类点,则 $u$ 就可以为 $2$ 类点。 ### 如何找一个 $1$ 类点 考虑以 $1$ 类点为根构成的 dfs 树的性质。观察其叶子,由于没有横叉边,所有叶子必然满足入度为 $1$。 对于所有入度为 $1$ 的点 $u$,假设有边 $v\to u$,则可以把 $u$ 向 $v$ 合并。具体来说,把 $u$ 的出边并到 $v$ 的出边里(所有 $u\to x$ 改为 $v\to x$,**不需要去除重边**,但要删去自环),然后删除点 $u$。也就是在 dfs 树上不断缩叶子的过程。 BFS 不断执行这个过程,并把新出现的入度为 $1$ 的点加入队列里。 在若干轮删除之后,若只剩下 $1$ 个点,则这个点即可作为 $1$ 类点;否则若某个时刻每个点入度都 $\ge 2$,则不存在 $1$ 类点。 使用启发式合并维护边集,时间复杂度 $O((n+m)\log n)$。 可以用 vector 存下每个点的入边和出边,合并时按两个 size 的和加权,取更小的点的所有边合并到大的点上,这样在合并时就可以计算有多少条 $u-v$ 的边。 ```cpp // what is matter? never mind. #pragma GCC optimize("Ofast") #pragma GCC optimize("unroll-loops") //#pragma GCC target("sse,sse2,sse3,sse4,popcnt,abm,mmx,avx,avx2") #include<bits/stdc++.h> #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) #define ll long long #define ull unsigned long long //#define int long long #define SZ(x) ((int)((x).size())) #define ALL(x) (x).begin(),(x).end() using namespace std; inline int read() { char c=getchar();int x=0;bool f=0; for(;!isdigit(c);c=getchar())f^=!(c^45); for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48); return f?-x:x; } #define fi first #define se second #define pb push_back #define mkp make_pair typedef pair<int,int>pii; typedef vector<int>vi; #define maxn 200005 #define inf 0x3f3f3f3f int n,m,rt; bool res1[maxn],res2[maxn]; int dfn[maxn],low[maxn],idx,scc,st[maxn],tp,bel[maxn]; vi e[maxn],ps[maxn]; void tar(int u){ dfn[u]=low[u]=++idx; st[++tp]=u; for(int v:e[u]){ if(!dfn[v])tar(v),low[u]=min(low[u],low[v]); else if(!bel[v])low[u]=min(low[u],dfn[v]); } if(dfn[u]!=low[u])return; int v; ++scc; do{ v=st[tp--]; bel[v]=scc; ps[scc].pb(v); } while(u!=v); } int q[maxn],hd,tl; bool vis[maxn]; bool bfs(int u){ For(i,1,n)vis[i]=0; q[hd=tl=1]=u; vis[u]=1; For(i,1,tl){ int x=q[i]; for(int y:e[x])if(!vis[y])vis[y]=1,q[++tl]=y; } For(i,1,n)if(!vis[i])return 0; return 1; } struct ufs{ int fa[maxn]; void init(int n){ For(i,0,n) fa[i]=i; } int gf(int x){ while(x!=fa[x])x=fa[x]=fa[fa[x]]; return x; } void mg(int u,int v){ fa[gf(u)]=gf(v); } bool chk(int u,int v){ return gf(u)==gf(v); } }U[2]; namespace fd{ vi e[maxn],re[maxn]; int deg[maxn]; int cnt,eu[maxn],ev[maxn]; void init(){ For(i,1,n)e[i].clear(),re[i].clear(),deg[i]=0; cnt=0; } void adde(int u,int v){ ++deg[v]; ++cnt,eu[cnt]=u,ev[cnt]=v; e[u].pb(cnt),re[v].pb(cnt); } ufs U; bool chk(int u,int v,int x,int y){ return (u==x&&v==y)||(u==y&&v==x); } void merge(int u,int v){ deg[v]+=deg[u]; if(SZ(re[u])+SZ(e[u])>SZ(re[v])+SZ(e[v])){ for(auto &i:e[v]) if(chk(U.gf(eu[i]),U.gf(ev[i]),u,v)) --deg[v]; for(auto &i:re[v])if(chk(U.gf(eu[i]),U.gf(ev[i]),u,v)) --deg[v]; swap(re[u],re[v]); swap(e[u],e[v]); }else{ for(auto &i:e[u]) if(chk(U.gf(eu[i]),U.gf(ev[i]),u,v)) --deg[v]; for(auto &i:re[u])if(chk(U.gf(eu[i]),U.gf(ev[i]),u,v)) --deg[v]; } for(auto &x:e[u]) e[v].pb(x); for(auto &x:re[u])re[v].pb(x); U.fa[u]=v; e[u].clear(),re[u].clear(),deg[u]=0; if(deg[v]<=1) q[++tl]=v; } int find(){ hd=1,tl=0; For(i,1,n)if(deg[i]<=1)q[++tl]=i; U.init(n); For(i,1,tl){ int u=q[i]; if(U.gf(u)==u && deg[u]==1){ int to=0,id=0; for(int i:re[u]) if(U.gf(eu[i])!=U.gf(u)) to=U.gf(eu[i]),id=i; re[u].clear(); re[u].pb(id); merge(u,to); } } return U.gf(1); } } int fa[maxn],dep[maxn]; vi e1[maxn],e2[maxn]; void dfs1(int u){ dfn[u]=low[u]=++idx; vis[u]=1; for(int v:e[u]){ if(bel[v]!=bel[u])continue; if(!dfn[v]) fa[v]=u,dep[v]=dep[u]+1,dfs1(v),low[u]=min(low[u],low[v]),e1[u].pb(v); else if(vis[v]) low[u]=min(low[u],dfn[v]),e2[u].pb(v); } vis[u]=0; } vi up[maxn],bak[maxn]; int cnt,eu[maxn],ev[maxn],bad[maxn]; void dfs2(int u){ for(auto v:e2[u]){ int i=++cnt; eu[i]=u,ev[i]=v; bad[i]=0; up[u].pb(i); bak[v].pb(i); } for(auto v:e1[u]){ dfs2(v); for(auto t:up[v]) up[u].pb(t); } sort(ALL(up[u]),[&](int x,int y){ return dep[ev[x]]<dep[ev[y]]; }); while(SZ(up[u]) && (SZ(up[u])>2 || dep[ev[up[u].back()]]>=dep[u])) up[u].pop_back(); } int cc[maxn]; void dfs3(int u){ if(u!=rt){ if(SZ(up[u])==1){ int z=up[u][0]; res1[u]=res1[ev[z]]; if(res1[u] && !bad[z]){ bad[z]=1; cc[eu[z]]+=1; cc[ev[z]]-=1; } } else res1[u]=0; } for(auto v:e1[u]) dfs3(v),cc[u]+=cc[v]; } void dfs4(int u){ if(res2[u]){ for(auto i:bak[u]){ int v=eu[i]; int w=bad[i]; for(int p=U[w].gf(v);dep[p]>dep[u];p=U[w].gf(p)){ if(cc[p]==w) res2[p]=1; U[w].fa[p]=fa[p]; } } } for(auto v:e1[u]) dfs4(v); } bool ok=0; void dfs0(int u){ if(!ok)return; ++cc[u]; if(cc[u]>1)return ok=0,void(); vis[u]=1; for(int v:e[u])if(!vis[v])dfs0(v); vis[u]=0; } bool chk1(int u){ For(i,1,n)cc[i]=0,vis[i]=0; ok=1; dfs0(u); if(!ok)return 0; For(i,1,n)if(cc[i]!=1)return 0; return 1; } void work(int O) { n=read(),m=read(); For(i,1,n)e[i].clear(),ps[i].clear(),dfn[i]=bel[i]=low[i]=0,res1[i]=res2[i]=0; idx=scc=tp=0; fd::init(); For(i,1,m){ int u=read(),v=read(); e[u].pb(v); fd::adde(u,v); } For(i,1,n)if(!dfn[i])tar(i); if(!bfs(ps[scc][0])){ For(i,1,n)cout<<'3';cout<<'\n'; return; } For(i,0,1)U[i].init(n); //rt=0; //For(i,1,n)if(chk1(i))rt=i; //cout<<"rt "<<rt<<endl; rt=fd::find(); if(!chk1(rt))rt=0; if(!rt){ For(i,1,n){ if(bel[i]==scc)cout<<'2'; else cout<<'3'; }cout<<'\n'; return; } For(i,1,n){ e1[i].clear(),e2[i].clear(),up[i].clear(),bak[i].clear(); cc[i]=0; dfn[i]=vis[i]=fa[i]=dep[i]=0; }cnt=0;idx=0,scc=0; res1[rt]=1; dfs1(rt); dfs2(rt); dfs3(rt); For(i,1,n)res2[i]=res1[i]; dfs4(rt); For(i,1,n){ if(res1[i])cout<<'1'; else if(res2[i])cout<<'2'; else cout<<'3'; } cout<<'\n'; } signed main() { int T=1; read();T=read(); For(_,1,T)work(_); return 0; } /* */ ```