最大流问题,FF算法:

链式前向星建边,异或来求反向边。

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
const int N=2e3+86;
struct edge{int to,nxt,w;}e[N*2];int hd[N],tot=1;
void add(int u,int v,int w){
e[++tot]=(edge){v,hd[u],w}; hd[u]=tot;
}
int n,m,x;
bitset<286> vis;

int dfs(int p,int flow){
if(p==n)
return flow;
vis[p]=1;
for(int eg=hd[p];eg;eg=e[eg].nxt){
int to=e[eg].to,vol=e[eg].w,c;
if(vol&&!vis[to]&&((c=dfs(to,min(vol,flow)))!=-1)){
e[eg].w-=c;
e[eg^1].w+=c;
return c;
}
}
return -1;
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m>>x;
f(i,1,m){
int x,y,val;
cin>>x>>y>>val;
add(x,y,val);
add(y,x,0);
}
LL ans=0,c;
while((c=dfs(1,3e8))!=-1){
vis.reset();
//cout<<c<<"\n";
ans+=c;
}
if(ans){
cout<<ans<<" ";
cout<<(x/ans+(x%ans?1:0));}
else cout<<"Orz Ni Jinan Saint Cow!";
return 0;
}

1
2
3
4
struct edge{int to,nxt,w;}e[N*2];int hd[N],tot=1;
void add(int u,int v,int w){
e[++tot]=(edge){v,hd[u],w}; hd[u]=tot;
}

第一问应该转化为求入度为0的点个数

第二问取入度为0的点个数与出度为0的点个数的较大值,注意特判仅有一个强连通分量的情况。

参考:题解 P2746 【[USACO5.3]校园网Network of Schools】


首先用Kosaraju求出各个强连通分量缩点,然后求出各个强连通分量间的有向边。
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
const int N = 186;
int n, cnt,mark[N],c,r;
vi v[N], rv[N], dfn, scc[N];
set<int> s[N], rs[N];
bitset<200> vis;

void dfs(int x)
{
vis[x] = 1;
for (auto it : v[x])
{
if (!vis[it])
dfs(it);
}
dfn.pb(x);
}

void dfs1(int x)
{
scc[cnt].pb(x);
mark[x] = cnt;
for (auto it : rv[x])
{
if (!mark[it])
dfs1(it);
}
}


int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
f(i, 1, n)
{
int x;
cin >> x;
while (x)
{
v[i].pb(x);
rv[x].pb(i);
cin >> x;
}
}
vis.reset();
f(i, 1, n)
{
if (!vis[i])
{
dfs(i);
}
}
for (auto it = dfn.rbegin(); it != dfn.rend(); it++)
{
if (!mark[*it])
{
cnt += 1;
dfs1(*it);
}
}
f(i, 1, n)
{
for (auto it : v[i])
{
if(mark[i]!=mark[it]){
s[mark[i]].insert(mark[it]);
}
}
for (auto it : rv[i])
{
if(mark[i]!=mark[it]){
rs[mark[i]].insert(mark[it]);
}
}
}
f(i,1,cnt){
if(!s[i].size())c+=1;
if(!rs[i].size())r+=1;
}
cout<<r<<"\n"<<(cnt==1?0:max(c,r));
return 0;
}

在计算科学中,Kosaraju的算法(又称为–Sharir Kosaraju算法)是一个线性时间(linear time)算法找到的有向图的强连通分量。它利用了一个事实,逆图(与各边方向相同的图形反转, transpose graph)有相同的强连通分量的原始图。

首先第一遍dfs扫一遍得到逆后序压倒栈dfn中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int vis[MAX];
vector<int> dfn;
f(i, 1, n){
if (!vis[i])
dfs(i);
}
void dfs(int x)
{
vis[x] = 1;
for (auto it : rim[x])//rim[x]这个vector中储存x连向其他点的边
{
if (!vis[it])
dfs(it);
}
dfn.pb(x);
}

然后FILO进行第二次dfs扫逆图,对于每次递归结束的点集构成一个强连通分量

要注意的是标记的mark不能为0,否则这处if (!mark[*it])会出错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int cnt;
void dfs1(int x)
{
mark[x] = cnt, t[cnt] += a[x];
//cout<<a[x]<<"jkj\n";
for (auto it : nrim[x])
{
if (!mark[it])
dfs1(it);
}
}
for (auto it = dfn.rbegin(); it != dfn.rend(); it++)
{
if (!mark[*it])
{
cnt+=1;
dfs1(*it);
}
}

缩点,将该有向有环图变为有向无环图,然后dp求解

因为已经变为有向无环图,所以其中号强连通分量的儿子


首先用Kosaraju找强连通分量,第一遍dfs遍历,并从后往前压入dfn栈中,然后再FILO进行第二遍dfs,对于每次遍历到的点均构成一个强连通分量。

然后用set容器储存各个强连通分量的边,用dp求解

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
const int N = 2e4;
int n, m, a[N], mark[N], t[N];
vi rim[N], nrim[N], dfn;
bitset<10086> vis;
set<int> s[N];
LL dp[N];

void dfs(int x)
{
vis[x] = 1;
for (auto it : rim[x])
{
if (!vis[it])
dfs(it);
}
dfn.pb(x);
}
int cnt;
void dfs1(int x)
{
mark[x] = cnt, t[cnt] += a[x];
//cout<<a[x]<<"jkj\n";
for (auto it : nrim[x])
{
if (!mark[it])
dfs1(it);
}
}
inline void df(int x)
{
if(dp[x])
return ;
for(auto it:s[x]){
df(it);
dp[x]=max(dp[x],dp[it]);
}
dp[x]+=t[x];
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
f(i, 1, n) cin >> a[i];
f(i, 1, m)
{
int x, y;
cin >> x >> y;
rim[x].pb(y);
nrim[y].pb(x);
}
vis.reset();
f(i, 1, n)
{
if (!vis[i])
dfs(i);
}
vis.reset();
for (auto it = dfn.rbegin(); it != dfn.rend(); it++)
{
if (!mark[*it])
{
cnt+=1;
dfs1(*it);
}
}
f(i, 1, n)
{
for (auto it : rim[i])
{
if (mark[i] != mark[it])
s[mark[i]].insert(mark[it]);
}
}
LL ans = 0;
f(i, 1, cnt )
{
df(i);
ans = max(ans, dp[i]);
}
cout << ans;
return 0;
}
0%