题解:P11712 「KTSC 2020 R2」冰战

· · 题解

题目描述

有一个图,问是否能删不超过两条边使得它变成一个二分图,如果不可以输出0,否则输出删除边数最少得方案数。

题目解答

发现这个图几乎是一个二分图,所以可以考虑先做一个二分图染色,得到了一棵 dfs 树,这棵树的非树边一定是返祖边。一条非树边可以和树边形成一个环,如果是奇环则称其为奇边,否则称其为偶边,显然如果没有奇边则直接输出 1 即可。

S 为所有奇边的集合对每条边 e,若 e 是一条非树边,则 T(e)=\{e\},否则设 e=(u,fa_u),定义 T(e) 是所有 u 子树到 u 子树外的返祖边的集合。

若最少删一条边

如果删的是非树边 e,则删的一定是唯一的一条奇边。

如果删的是树边 e=(u,fa_u),显然 S\subset T(e),而如果 T(e)\neq S,任取偶边 e_1\in T(u)\setminus S,奇边 e_2\in T(u)\cap Se_1,e_2 和若干条(偶数条)依然构成一个奇环。

综上,删一条边的情况等价于 \exist e\ s.t. \ T(e)=S

若最少删两条边

设删去 x_1,x_2 两条边,设 A_i=T(x_i)\cap S,B_i=T(x_i)\setminus S,i=1,2

显然 A_i,B_i 均非空,否则可以删一条边解决。

任取 e_1\in A_1,e_2\in B_1e_1,e_2 和树边构成的奇环并没有因为 x_1 切断,因此 x_2 一定切断了这个奇环,这也就意味着对于 x_2 来说,e_2 是跨过这条边的返祖边,即 e_2\in B_2,同时如果 e_1\in A_2,则这个奇环还是没有切断,因此 e_1\not\in A_2,即 B_1\subset B_2,A_1\cap A_2=\emptyset

e_1\in A_2,e_2\in B_2 结论同样成立,因此 B_1=B_2。 又显然任意 e\in S 他至少在 A_1,A_2 之一里,所以可以删两条边的情况等价于:

**程序实现** 发现维护集合是复杂度非常不合理的,可以考虑哈希,注意到我们只需要维护集合,并且需要实现集合的加减等操作,因此可以考虑异或哈希,给每条边随机权值 $h_e$,集合 $S$ 的哈希值 $hash(S)=\oplus_{e\in S}h_e$。 ```cpp mt19937_64 rnd(time(0)); int head[N],ecnt; int col[N],dep[N]; ull S,T[N]; bool vis[N]; map<ull,int> cnt; struct Edge{ int to,next; }e[N<<1]; void add(int x,int y){ e[++ecnt]=(Edge){y,head[x]},head[x]=ecnt; } void dfs(int u,int fa){ vis[u]=true; RepG(i,u){ int v=e[i].to; if(v==fa)continue; if(!vis[v]){ col[v]=col[u]^1; dep[v]=dep[u]+1; dfs(v,u); T[u]^=T[v]; } else if(dep[v]<dep[u]){ ull x=rnd(); T[u]^=x; T[v]^=x; if(col[u]==col[v])S^=x; cnt[x]++; } } cnt[T[u]]++; } long long count_ways(int n, std::vector<int> U, std::vector<int> V) { for(int i=0;i<U.size();i++)add(U[i],V[i]),add(V[i],U[i]); dfs(1,0); if(!S)return 1; else if(cnt[S])return cnt[S]; else{ ll res=0; for(auto [x,t]:cnt)res+=1ll*t*cnt[x^S]; return res/2; } } ```