最短路

这部分用wiki的即可

网络瘤

最大流

dinic

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
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
#include<vector>
#include<set>
#include<map>
#include<algorithm>

using namespace std;

const int maxn=20020;

int head[maxn],size=1,st,ed,ans,n,m;
int dep[maxn],vis[maxn];
int nt[maxn];

struct edge{
int next,to,dis;
int flow;
}e[maxn*10];

inline void addedge(int next,int to,int flow)
{
e[++size].to=to;
e[size].next=head[next];
e[size].flow=flow;
head[next]=size;
}

inline int bfs(int st)
{
queue <int> q;
memset(vis,0,sizeof(vis));
memset(dep,0,sizeof(dep));
q.push(st);
vis[st]=1;
while(!q.empty())
{
int t=q.front();
q.pop();
int i,j,k;
for(i=head[t];i;i=e[i].next)
{
j=e[i].to;
k=e[i].flow;
if(vis[j]||dep[j]||k<=0) continue;
dep[j]=dep[t]+1;q.push(j);
vis[j]=1;
}
}
return vis[ed];
}

int dfs(int t,int nowt)
{
if(t==ed||!nowt) return nowt;
int tot=0;
int j,k;
for(int &i=nt[t];i;i=e[i].next)
{
j=e[i].to;
k=e[i].flow;
if(k>0&&dep[j]==dep[t]+1)
{
int f=dfs(j,min(k,nowt-tot));
e[i].flow-=f;
e[i^1].flow+=f;
tot+=f;
if(nowt==tot) return tot;
}
}
return tot;

}

void dinic()
{
int i;
while(bfs(st))
{
for(i=1;i<=n;i++) nt[i]=head[i];
ans+=dfs(st,0x3f3f3f3f);
}
}


int main()
{
ios::sync_with_stdio(false);
register int i,j;
cin>>n>>m>>st>>ed;
int t1,t2,t3;
for(i=1;i<=m;i++)
{
cin>>t1>>t2>>t3;
addedge(t1,t2,t3);
addedge(t2,t1,0);
}
dinic();
cout<<ans<<endl;
return 0;

}

isap

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
#include<stdio.h>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<vector>
#include<set>
#include<map>
#include<algorithm>

#define int long long

using namespace std;

const int maxn=5200;

int head[maxn],dep[maxn],size=1,nhead[maxn];
int n,m,st,ed;
int gap[maxn];

struct edge{
int next,to,flow;
}e[maxn<<1];

inline void addedge(int next,int to,int flow)
{
e[++size].to=to;
e[size].flow=flow;
e[size].next=head[next];
head[next]=size;
e[++size].to=next;
e[size].flow=0;
e[size].next=head[to];
head[to]=size;
}

inline void getdis()
{
queue <int> q;
q.push(ed);
memset(gap,0,sizeof(gap)); memset(dep,0,sizeof(dep));
for(int i=1;i<=n;i++) nhead[i]=head[i];
++gap[dep[ed]=1];
while(!q.empty())
{
int t=q.front();
q.pop();
int i,j,k;
for(i=head[t];i;i=e[i].next)
{
j=e[i].to;
if(!dep[j]) dep[j]=dep[t]+1,gap[dep[j]]++,q.push(j);
}
}
}

int isap(int t,int nowflow)
{
if(t==ed||!nowflow) return nowflow;
int j,k,f,tot=0;
for(int &i=nhead[t];i;i=e[i].next)
{
j=e[i].to; k=e[i].flow;
if(dep[j]==dep[t]-1)
{
f=isap(j,min(nowflow-tot,k));
e[i].flow-=f;
e[i^1].flow+=f;
tot+=f;
if(tot==nowflow) return tot;
}
}
if(!(--gap[dep[t]])) dep[st]=n+1;
++gap[++dep[t]];
nhead[t]=head[t];
return tot;
}

inline int getans()
{
int ans=0;
getdis();
ans=isap(st,0x3f3f3f3f);
while(dep[st]<=n) ans+=isap(st,0x3f3f3f3f);
return ans;
//cout<<ans<<endl;
}

signed main()
{
ios::sync_with_stdio(false);
register int i,j;
cin>>n>>m>>st>>ed;
int t1,t2,t3;
for(i=1;i<=m;i++)
{
cin>>t1>>t2>>t3;
addedge(t1,t2,t3);
}
cout<<getans()<<endl;
return 0;
}

费用流

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
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
#include<vector>
#include<set>
#include<map>
#include<algorithm>

using namespace std;

const int maxn=10080;

int head[maxn],nt[maxn],size=1,mcost,mflow;
int pre[maxn],las[maxn],flow[maxn];
int vis[maxn],dis[maxn];
int st,ed,n,m;

struct node{
int next,to,dis,flow;
}e[maxn*20];

inline void addedge(int next,int to,int dis,int flow)
{
e[++size].to=to;
e[size].dis=dis;
e[size].flow=flow;
e[size].next=head[next];
head[next]=size;
}

inline int spfa()
{
queue <int> q;
memset(vis,0,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
memset(flow,0x3f,sizeof(flow));
memset(pre,0,sizeof(pre));
dis[st]=0;
vis[st]=1;
las[st]=0;
pre[st]=0;
q.push(st);
while(!q.empty())
{
int t=q.front();
q.pop();
vis[t]=0;
int i,j,k,l;
for(i=head[t];i;i=e[i].next)
{
j=e[i].to;
k=e[i].dis;
l=e[i].flow;
if(dis[t]+k<dis[j]&&l>0)
{
flow[j]=min(l,flow[t]);
dis[j]=dis[t]+k;
pre[j]=t;
las[j]=i;
if(!vis[j]) vis[j]=1,q.push(j);
}

}
}
return pre[ed];
}
inline void mcmf()
{
int i;
while(spfa())
{
int t=ed;
mflow+=flow[ed];
mcost+=dis[ed]*flow[ed];
for(i=ed;i!=st;i=pre[i])
{
e[las[i]].flow-=flow[ed];
e[las[i]^1].flow+=flow[ed];
}
}
}

int main()
{
ios::sync_with_stdio(false);
register int i,j;
cin>>n>>m>>st>>ed;
int t1,t2,t3,t4;
for(i=1;i<=m;i++)
{
cin>>t1>>t2>>t3>>t4;
addedge(t1,t2,t4,t3);
addedge(t2,t1,-t4,0);
}
mcmf();
cout<<mflow<<" "<<mcost;
}

二分图

二分图最大权完美匹配,KM

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
#include<stdio.h>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<vector>
#include<set>
#include<map>
#include<algorithm>

#define int long long

using namespace std;

const int maxn=550;

int dis[maxn][maxn],match[maxn];
int etx[maxn],ety[maxn];
int slack[maxn],pre[maxn],vis[maxn];
int n,m,ans;

void bfs(int st)
{
memset(pre,0,sizeof(pre));
memset(slack,0x3f,sizeof(slack));
int t=0,tt=0;
match[tt]=st;
int i;
do
{
int dmmn=0x3f3f3f3f3f3f3f3f;
t=match[tt];
vis[tt]=1;
int nxt=0;
for(i=1;i<=n;i++)
{
if(vis[i]) continue;
if(slack[i]>etx[t]+ety[i]-dis[t][i]) slack[i]=etx[t]+ety[i]-dis[t][i],pre[i]=tt;
if(slack[i]<dmmn) dmmn=slack[i],nxt=i;
}
for(i=0;i<=n;i++)
{
if(vis[i]) etx[match[i]]-=dmmn,ety[i]+=dmmn;
else slack[i]-=dmmn;
}
tt=nxt;
}while(match[tt]);
while(tt) match[tt]=match[pre[tt]],tt=pre[tt];
}

void km()
{
memset(etx,0,sizeof(etx));
memset(ety,0,sizeof(ety));
memset(match,0,sizeof(match));
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof(vis));
bfs(i);
}
for(int i=1;i<=n;i++) ans+=dis[match[i]][i];
cout<<ans<<endl;
for(int i=1;i<=n;i++) cout<<match[i]<<" ";
return ;
}

signed main()
{
ios::sync_with_stdio(false);
register int i,j;
cin>>n>>m;
int t1,t2,t3;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
dis[i][j]=-0x3f3f3f3f3f3f3f3f;
for(i=1;i<=m;i++)
{
cin>>t1>>t2>>t3;
dis[t1][t2]=t3;
}
km();
return 0;
}

二分图最大匹配

匈牙利

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
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
#include<vector>

using namespace std;

int head[10086],size,n,m,ee,have_meizi[10086],tot,vis[100860];

struct edge{
int next,to;
}e[1008600];

void addedge(int next,int to)
{
e[++size].next=head[next];
e[size].to=to;
head[next]=size;
}

int ycl(int a)
{
return a+n;
}

int hungary(int s)
{
int i,j;
for(i=head[s];i;i=e[i].next)
{
j=e[i].to;
if(vis[j]) continue;
vis[j]=1;
if(!have_meizi[j])
{
have_meizi[j]=s;
return 1;
}
else
{
if(hungary(have_meizi[j]))
{
have_meizi[j]=s;
return 1;
}
}
}
return 0;
}

int main()
{
int i,j;
scanf("%d %d %d",&n,&m,&ee);
for(i=1;i<=ee;i++)
{
int t1,t2;
scanf("%d %d",&t1,&t2);
if(t1>n||t2>m||t1>m||t2>n) continue;
//t2=ycl(t2);
addedge(t1,t2);
}
for(i=1;i<=m;i++)
{
memset(vis,0,sizeof(vis));
if(hungary(i)) tot++;
}
printf("%d",tot);
return 0;
}

prufer

【模板】Prufer 序列

题目描述

请实现 Prufer 序列和无根树的相互转化。

为方便你实现代码,尽管是无根树,我们在读入时仍将 nn 设为其根。

对于一棵无根树,设 f1n1f_{1\dots n-1} 为其父亲序列fif_i 表示 iinn 为根时的父亲),设 p1n2p_{1 \dots n-2} 为其 Prufer 序列

另外,对于一个长度为 mm 的序列 a1ma_{1 \dots m},我们设其权值xori=1mi×ai\operatorname{xor}_{i = 1}^m i \times a_i

输入格式

第一行两个整数 n,mn,m,表示树的点数和转化类型。

m=1m = 1,第二行一行 n1n-1 个整数,表示父亲序列。
m=2m = 2,第二行一行 n2n-2 个整数,表示 Prufer 序列。

输出格式

m=1m = 1,一行一个整数,表示给出的父亲序列对应的 Prufer 序列的权值。
m=2m = 2,一行一个整数,表示给出的 Prufer 序列对应的父亲序列的权值。

样例 #1

样例输入 #1

1
2
6 1
3 6 4 6 1

样例输出 #1

1
29

样例 #2

样例输入 #2

1
2
6 2
4 6 5 2

样例输出 #2

1
4

prufer编码

cayleycayley公式[n][n]含有nn2n^{n-2}棵树

pruferprufer编码,f(T)=(a1,a2,...,an2)f(T)=(a_1,a_2,...,a_{n-2}),aia_i表示编号最小的叶节点相邻节点(动态拆树)

显然这这东西对应回去的树一一对应,因此nn2n^{n-2}恰好对应了cayleycayley公式(实际上这东西就是用来证明cayleycayley的)

这东西证明cayleycayley要证明一一对应的关系(双射)

考虑如何构造,发现当前位置的pruferprufer序列是最小的叶子节点,我们把叶子节点都先丢进一个堆,然后每次取堆首的时候顺便把他的父亲放进堆里面,时间复杂度O(nlogn)O(nlogn)事实证明本题的瓶颈还是在读入上面,所以把读入整好了也是能过的(

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
inline void getpprufer()
{
int i;
for(i=1;i<=n-1;i++) d[a[i]]++;
priority_queue <int> q;
for(i=1;i<=n-1;i++) if(!d[i]) q.push(-i);
int cnt=0;
while(!q.empty())
{
if(cnt==n-2) break;
int t=q.top();
t=-t;
q.pop();
prufer[++cnt]=a[t];
d[a[t]]--;
if(!d[a[t]]) q.push(-a[t]);
}
int ans=0;
for(i=1;i<=n-2;i++) ans^=(i*prufer[i]);
cout<<ans<<endl;
}

考虑还原这棵树,不难发现不在pruferprufer序列上面的点一定是叶子节点(度为1),我们每次就找到最小的叶子节点,然后就可以根据pruferprufer序列来还原(得到父亲)

这样时间复杂度太高了,实际上我们每次找第一个的时候就只有两种情况,第一个更小和更大,更大的话很好做,直接用一个指针扫,变小就直接一个while循环到变大或者结束,时间复杂度是O(N)O(N)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
inline void getfat()
{
//a为prufer序列
int lok=1; //lok相当于一个指针
int i;
for(i=1;i<=n-2;i++) d[a[i]]++; //这里的算度的时候不考虑自己与父亲的连边
a[n-1]=n; //体面给了,n是根
for(i=1;i<=n-2;i++)
{
while(d[lok]) lok++;
f[lok]=a[i];
vis[lok]=1;
while(i<n&&!--d[a[i]]&&lok>=a[i]) //注意循环顺序
{
i++;
f[a[i-1]]=a[i];
}
lok++;
}
int ans=0;
for(i=1;i<=n-1;i++) ans^=(i*f[i]);
cout<<ans<<endl;
}

然后接着想你就会发现,这东西不仅可以用来做pruferprufer还原ff,同理还可以用来构造pruferprufer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
inline void getprufer()
{
int i;
int lok=1;
for(i=1;i<=n-1;i++) d[a[i]]++; //还是一样的,找到每个点的度
for(i=1;i<=n-2;i++)
{
while(d[lok]) lok++; //依旧是扫一下
prufer[i]=a[lok];//a[]记录的是父亲节点
while(i<n-1&&!--d[prufer[i]]&&prufer[i]<lok) //这里的条件要变一下
{
i++;
prufer[i]=a[prufer[i-1]];
}
lok++;
}
int ans=0;
for(i=1;i<=n-2;i++) ans^=(i*prufer[i]);
cout<<ans<<endl;
}
s