每个点取值拆成多个点的最小割问题:CF1430G

本文搬运自本人高中时期CSDN博客,若图片加载不出来,可到原文查看:https://blog.csdn.net/zhangtingxiqwq/article/details/134751924

https://vj.imken.moe/contest/597216#problem/I

题目等价于求 minuau(outuinu)\min \sum_{u}a_u(out_u-in_u)

发现每个数的取值范围最多到 nn ,然后又有一堆限制,考虑拆点+网络流。

每个点拆成 n+2n+2 个点(分别代表取值从 0n+10\sim n+1 ),代表他的 nn 种取值,然后我们现在要是代价最小,我们考虑最小割,考虑下图连边方式:

在这里插入图片描述

我们考虑这个点选了 3(k)3(k) ,看一下割的效果

在这里插入图片描述

也就是 k×outu+(n+1k)×inu=k×(outuinu)+(n+1)×inuk\times out_u+(n+1-k)\times in_u=k\times (out_u-in_u)+(n+1)\times in_u

现在我们还要满足 au>ava_u>a_v ,我们在所以 (u,k)(v,k1)(u,k)\to(v,k-1) 中连边即可。

然后构造最小割输出即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include<bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stdout, ##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
#define int long long
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<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//srand(time(0));
#define N 100010
//#define M
//#define mo
namespace Flow {
#define int long long
struct mf_graph {
struct node {
int x, y, z, n;
};
vector<node>d;
vector<int>h, H, dep;
queue<int>q;
int k;
int u, v, w, S, T, ans=0;
void reset(int n) {
h.resize(n+5); k=1; d.resize(2);
H.resize(n+5); dep.resize(n+5);
}
void cun(int x, int y, int z) {
++k; d.pb({x, y, z, h[x]});
d[k].n=h[x]; h[x]=k;
}
void add_edge(int x, int y, int z) {
if(x!=1 && y!=2)debug("%lld %lld %lld\n", x, y, z);
// debug("%lld -> %lld %lld\n", x, y, z);
cun(x, y, z); cun(y, x, 0);
}
int bfs() {
while(!q.empty()) q.pop();
fill(dep.begin(), dep.end(), -1);
h=H;
dep[S]=1; q.push(S);
while(!q.empty()) {
u=q.front(); q.pop();
for(int g=h[u]; g; g=d[g].n) {
v=d[g].y; w=d[g].z;
if(w<=0 || dep[v]!=-1) continue;
dep[v]=dep[u]+1; q.push(v);
}
}
return dep[T]!=-1;
}
int dfs(int x, int w) {
if(x==T) return w;
if(!w) return 0;
int ans=0, s;
for(int &i=h[x]; i; i=d[i].n) {
int y=d[i].y, z=d[i].z;
if(dep[y]!=dep[x]+1) continue;
if(z<=0) continue;
s=dfs(y, min(w, z)); ans+=s; w-=s;
d[i].z-=s; d[i^1].z+=s;
if(!w) break;
}
return ans;
}
int flow(int SS, int TT) {
S=SS; T=TT; H=h;
while(bfs()) ans+=dfs(S, 1e18);
return ans;
}
};
#undef int
}
using namespace Flow;
int n, m, i, j, k, T, S;
int ans[N], u, v, w, Out[N], In[N];

int nd(int i, int j) {
return i*100+j;
}

signed main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
// T=read();
// while(T--) {
//
// }
n=read(); m=read();
mf_graph G; G.reset(1000010);
S=1; T=2;
for(i=1; i<=m; ++i) {
u=read(); v=read(); w=read();
Out[u]+=w; In[v]+=w; k-=In[v];
for(j=1; j<=n+1; ++j)
G.add_edge(nd(u, j), nd(v, j-1), 1e18);
}
for(i=1; i<=n; ++i) {
for(j=0; j<=n+1; ++j) G.add_edge(S, nd(i, j), Out[i]);
for(j=0; j<=n+1; ++j) G.add_edge(nd(i, j), T, In[i]);
for(j=0; j<=n; ++j) G.add_edge(nd(i, j), nd(i, j+1), 1e18);
G.add_edge(S, nd(i, n+1), 1e18); G.add_edge(nd(i, 0), T, 1e18);
}
k*=(n+1); k+=G.flow(S, T);
debug(">> %lld\n", k);
G.bfs();
for(i=1; i<=n; ++i) {
for(j=n; j>=0; --j) if(G.dep[nd(i, j)]==-1) break;
ans[i]=j;
}
for(i=1; i<=n; ++i) printf("%lld ", ans[i]);
return 0;
}