题解 [CF1264E] Beautiful League/[WC2007] 剪刀石头布
[CF1264E] Beautiful League
[WC2007] 剪刀石头布
真·网络流看题解乐趣尽失
- 关于有向图三元环定向/计数:
有一个巧妙的性质是合法三元环对于所有三条边(不考虑方向)补集转化后
发现每个不合法方案中一定存在且仅存在一个点的出度为 2
在完全图中尤其有用,可以直接转化为 \(\dbinom{n}{3}-\sum\dbinom{out_i}{2}\)
那么这个题就可以用这种方法来完成定向:
发现就是要不合法三元环尽量少
就是要让 \(\sum\frac{out_i(out_i-1)}{2}\) 尽量小
\[\begin{aligned}
\sum\frac{out_i(out_i-1)}{2}&=\sum\frac{out_i^2-out_i}{2}\\
&=\frac{(\sum out_i^2)-总边数}{2}
\end{aligned}\]
于是转为最小化 \(\sum out_i^2\)
那么每个点的费用函数都是凸的,每条边向与之相关的两个点之一产生 1 的流量
大力费用流即可
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define ll 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;
queue<int> q;
bool vis[N], lck[N], ans[55][55];
int head[N], mp[55][55], dis[N], inc[N], back[N], eid[1550][55], ecnt=1, s, t, tot;
struct edge{int to, next, flw, cst;}e[N<<1];
inline void add(int s, int t, int f, int c) {e[++ecnt]={t, head[s], f, c}; head[s]=ecnt;}
bool spfa(int s, int t) {
memset(dis, 0x3f, sizeof(dis));
memset(back, -1, sizeof(back));
dis[s]=0; inc[s]=INF;
q.push(s);
while (q.size()) {
int u=q.front(); q.pop();
vis[u]=0;
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
if (e[i].flw && dis[u]+e[i].cst<dis[v]) {
dis[v]=dis[u]+e[i].cst;
back[v]=i; inc[v]=min(inc[u], e[i].flw);
if (!vis[v]) q.push(v), vis[v]=1;
}
}
}
return ~back[t];
}
signed main()
{
tot=n=read(); m=read();
memset(head, -1, sizeof(head));
for (int i=1; i<=n; ++i)
for (int j=i+1; j<=n; ++j)
mp[i][j]=mp[j][i]=++tot;
s=++tot, t=++tot;
for (int i=1,u,v,id; i<=m; ++i) {
u=read(); v=read();
add(mp[u][v], v, 1, 0), add(v, mp[u][v], 0, 0);
ans[u][v]=1;
lck[mp[u][v]]=1;
}
for (int i=1; i<=n; ++i) {
for (int j=i+1; j<=n; ++j) {
add(s, mp[i][j], 1, 0), add(mp[i][j], s, 0, 0);
if (!lck[mp[i][j]]) {
eid[mp[i][j]][i]=ecnt+1;
add(mp[i][j], i, 1, 0), add(i, mp[i][j], 0, 0);
eid[mp[i][j]][j]=ecnt+1;
add(mp[i][j], j, 1, 0), add(j, mp[i][j], 0, 0);
}
}
}
for (int i=1; i<=n; ++i)
for (int j=1; j<=n; ++j)
add(i, t, 1, (j-1)<<1|1), add(t, i, 0, -((j-1)<<1|1));
memset(vis, 0, sizeof(vis));
while (spfa(s, t)) {
// ans+=inc[t]*dis[t];
for (int u=t; u!=s; u=e[back[u]^1].to) {
e[back[u]].flw-=inc[t];
e[back[u]^1].flw+=inc[t];
}
}
for (int i=1; i<=n; ++i) {
for (int j=i+1; j<=n; ++j) if (!lck[mp[i][j]]) {
// cout<<"ij: "<<i<<' '<<j<<endl;
if (!e[eid[mp[i][j]][i]].flw) ans[j][i]=1;
if (!e[eid[mp[i][j]][j]].flw) ans[i][j]=1;
}
}
// cout<<"tem: "<<tem<<endl;
// cout<<"ans: "<<ans<<endl;
// ans=n*(n-1)*(n-2)/6-(ans-n*(n-1)/2)/2;
// cout<<ans<<endl;
for (int i=1; i<=n; ++i) {
for (int j=1; j<=n; ++j) printf("%d", ans[i][j]);
printf("\n");
}
return 0;
}