题解 签到题
关于我因为不会最小割输出方案了导致考场上没打出来最小割树这件事
- 关于最小割输出方案:
一种可行的方案是 BFS 找到残量网络中所有与 S 连通的点
若有满流边的恰好一个端点与 S 连通,则将这条边割断
然后正解:
考虑题面里那个 \(deg\leqslant 3\) 怎么用
发现这代表了最小割 \(\leqslant 3\)
于是 \(=0\) 方案数可以直接并查集
\(=1\) 方案数可以 tarjan 缩点
接下来 \(=2, =3\) 的方案数只要算出来一个就能减出来另一个
根据题解可以发现 \(\operatorname{mincut}(a, b)=3,\operatorname{mincut}(b, c)=3\),则有 \(\operatorname{mincut}(a, c)=3\)
也就是说可以将最小割为 3 这个东西有传递性(题解说形成一个等价类?)
那我们考虑把这些最小割为 3 的点缩起来
直接缩不好缩,但我们可以把能用两条边割开的连通块都割开
于是一个暴力是枚举两条边(可以发现只能是 祖孙/树边-非树边)
- 一个技巧:将反祖边的权值放到两个端点上
则每个节点子树的异或和就是跨过这个点的反祖边的权值异或和
于是可以枚举树边,算出跨过这个点的反祖边的权值异或和
那就可以用 map 确定是否只有一条这样的边
然后枚举两条边,若跨过这两个点的反祖边的权值异或和分别相等,
那大概就是这样的情况,也可以断开
然后考虑优化,发现对于同样深度的点,只断深度相邻的也是等价的
(若有 a,b,c 深度递增,则断了 (a,b) (b,c) 就不用管 (a, c) 了
于是就可以优化到 \(O(n)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 3000010
#define fir first
#define sec second
#define pb push_back
#define ll long long
#define ull unsigned long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m;
pair<int, int> g[N];
namespace force{
int head[N], ecnt;
struct edge{int to, next, val;}e[N<<1];
inline void add(int s, int t, int w) {e[++ecnt]={t, head[s], w}; head[s]=ecnt;}
int cur[N], dep[N], S, T;
ll ans;
bool bfs() {
memset(dep, 0, sizeof(dep));
queue<int> q;
q.push(S);
dep[S]=1; cur[S]=head[S];
int u;
while (q.size()) {
u=q.front(); q.pop();
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
if (e[i].val && !dep[v]) {
dep[v]=dep[u]+1;
cur[v]=head[v];
if (v==T) return 1;
q.push(v);
}
}
}
return 0;
}
int dfs(int u, int in) {
if (u==T||!in) return in;
int rest=in, tem;
for (int i=cur[u],v; ~i; cur[u]=i=e[i].next) {
v = e[i].to;
if (e[i].val && dep[v]==dep[u]+1) {
tem=dfs(v, min(rest, e[i].val));
rest-=tem;
e[i].val-=tem;
e[i^1].val+=tem;
if (!rest) break;
}
}
return in-rest;
}
ll dinic(int s, int t) {
memset(head, -1, sizeof(head)); ecnt=1;
for (int i=1; i<=m; ++i) {
add(g[i].fir, g[i].sec, 1), add(g[i].sec, g[i].fir, 0);
add(g[i].sec, g[i].fir, 1), add(g[i].fir, g[i].sec, 0);
}
add(S, s, INF), add(s, S, 0);
add(t, T, INF), add(T, t, 0);
ll tot=0;
while (bfs()) tot+=dfs(S, INF);
return tot;
}
void solve() {
S=n+1, T=n+2;
for (int i=1; i<=n; ++i)
for (int j=i+1; j<=n; ++j)
ans+=dinic(i, j);
cout<<ans<<endl;
}
}
namespace task{
random_device seed;
mt19937_64 rand(seed());
vector<int> ecc[N];
unordered_map<ull, int> mp, sta, buc;
bool vis[N], cut[N], intr[N];
ll rnd[N], val[N], tag[N], ans, num;
int head[N], dsu[N], usiz[N], bel[N], siz[N], dfn[N], low[N], ecnt=1, tot, col, rot;
struct edge{int to, next, id;}e[N<<1];
inline void add(int s, int t, int w) {e[++ecnt]={t, head[s], w}; head[s]=ecnt;}
inline int find(int p) {return dsu[p]==p?p:dsu[p]=find(dsu[p]);}
inline ll sqr(ll x) {return x*x;}
void tarjan(int u, int in_edge) {
dfn[u]=low[u]=++tot;
in_edge^=1;
for (int i=head[u],v; ~i; i=e[i].next) if (i!=in_edge) {
v = e[i].to;
if (!dfn[v]) {
tarjan(v, i);
if (dfn[u]<low[v]) cut[i]=cut[i^1]=1;
else low[u]=min(low[u], low[v]);
}
else low[u]=min(low[u], dfn[v]);
}
}
void dfs1(int u) {
bel[u]=col; ++siz[col]; ecc[col].pb(u);
for (int i=head[u]; ~i; i=e[i].next)
if (!cut[i] && !bel[e[i].to]) dfs1(e[i].to);
}
void dfs2(int u, int in_edge) {
//cout<<"dfs2: "<<u<<endl;
vis[u]=1;
in_edge^=1;
for (int i=head[u],v; ~i; i=e[i].next) if (!cut[i]) {
v = e[i].to;
if (vis[v]) {if (i!=in_edge) val[u]^=rnd[e[i].id]; continue;}
intr[i]=intr[i^1]=1;
dfs2(v, i);
val[u]^=val[v];
}
}
void dfs3(int u, int in_edge) {
//cout<<"dfs3: "<<u<<' '<<in_edge<<endl;
in_edge^=1;
for (int i=head[u],v; ~i; i=e[i].next) if (intr[i] && i!=in_edge) {
v = e[i].to;
if (mp.find(val[v])!=mp.end()) {
//cout<<"pos1: "<<endl;
ll x=rand(), y=rand();
tag[rot]^=x; tag[v]^=x^y;
}
if (sta.find(val[v])!=sta.end()) {
//cout<<"pos2: "<<endl;
ll x=rand(), y=rand();
tag[rot]^=x; tag[sta[val[v]]]^=x^y; tag[v]^=x^y;
}
sta[val[v]]=v;
dfs3(v, i);
sta.erase(val[v]);
}
}
void dfs4(int u, int in_edge) {
in_edge^=1;
for (int i=head[u],v; ~i; i=e[i].next) if (intr[i] && i!=in_edge) {
v = e[i].to;
tag[v]^=tag[u];
dfs4(v, i);
}
}
void solve() {
num=1ll*n*(n-1);
memset(head, -1, sizeof(head));
for (int i=1; i<=n; ++i) dsu[i]=i, usiz[i]=1;
for (int i=1; i<=m; ++i) {
int s=g[i].fir, t=g[i].sec;
add(s, t, i); add(t, s, i);
mp[rnd[i]=rand()]=i;
s=find(s); t=find(t);
if (s==t) continue;
dsu[s]=t; usiz[t]+=usiz[s];
}
//cout<<"rnd: "; for (int i=1; i<=m; ++i) cout<<rnd[i]<<' '; cout<<endl;
//cout<<"val: "; for (int i=1; i<=n; ++i) cout<<val[i]<<' '; cout<<endl;
for (int i=1; i<=n; ++i) if (find(i)==i) num-=1ll*usiz[i]*(n-usiz[i]);
//cout<<"num: "<<num<<endl;
for (int i=1; i<=n; ++i) if (!dfn[i]) tarjan(i, 0);
for (int i=1; i<=n; ++i) if (!bel[i]) ++col, dfs1(i);
//cout<<"cut: "; for (int i=1; i<=m; ++i) cout<<cut[i]<<' '; cout<<endl;
//cout<<"intr: "; for (int i=1; i<=m; ++i) cout<<intr[i]<<' '; cout<<endl;
//cout<<"bel: "; for (int i=1; i<=n; ++i) cout<<bel[i]<<' '; cout<<endl;
for (int i=1; i<=col; ++i) {
//cout<<"i: "<<i<<endl;
ll t=usiz[find(rot=ecc[i][0])];
ans+=1ll*siz[i]*(t-siz[i]);
num-=1ll*siz[i]*(t-siz[i]);
for (auto it:ecc[i]) tag[it]=val[it]=0;
tag[rot]=rand();
dfs2(ecc[i][0], 0); dfs3(ecc[i][0], 0); dfs4(ecc[i][0], 0);
}
//cout<<"val: "; for (int i=1; i<=n; ++i) cout<<val[i]<<' '; cout<<endl;
//cout<<"num: "<<num<<endl;
//cout<<"tag: "; for (int i=1; i<=n; ++i) cout<<tag[i]<<' '; cout<<endl;
for (int i=1; i<=n; ++i) ++buc[tag[i]];
for (auto it:buc) ans+=3ll*it.sec*(it.sec-1), num-=1ll*it.sec*(it.sec-1);
ans+=2ll*num;
cout<<ans/2<<endl;
}
}
signed main()
{
freopen("juice.in", "r", stdin);
freopen("juice.out", "w", stdout);
n=read(); m=read();
for (int i=1; i<=m; ++i) g[i].fir=read(), g[i].sec=read();
//force::solve();
task::solve();
return 0;
}