最短路
这部分用wiki的即可
网络瘤
最大流
dinic
| 12
 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
| 12
 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;
 
 }
 
 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;
 }
 
 | 
费用流
| 12
 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
| 12
 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;
 }
 
 | 
二分图最大匹配
匈牙利
| 12
 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;
 
 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 序列和无根树的相互转化。
为方便你实现代码,尽管是无根树,我们在读入时仍将 n 设为其根。
对于一棵无根树,设 f1…n−1 为其父亲序列(fi 表示 i 在 n 为根时的父亲),设 p1…n−2 为其 Prufer 序列。
另外,对于一个长度为 m 的序列 a1…m,我们设其权值为 xori=1mi×ai。
输入格式
第一行两个整数 n,m,表示树的点数和转化类型。
若 m=1,第二行一行 n−1 个整数,表示父亲序列。
若 m=2,第二行一行 n−2 个整数,表示 Prufer 序列。
输出格式
若 m=1,一行一个整数,表示给出的父亲序列对应的 Prufer 序列的权值。
若 m=2,一行一个整数,表示给出的 Prufer 序列对应的父亲序列的权值。
样例 #1
样例输入 #1
样例输出 #1
样例 #2
样例输入 #2
样例输出 #2
prufer编码
cayley公式[n]含有nn−2棵树
prufer编码,f(T)=(a1,a2,...,an−2),ai表示编号最小的叶节点相邻节点(动态拆树)
显然这这东西对应回去的树一一对应,因此nn−2恰好对应了cayley公式(实际上这东西就是用来证明cayley的)
这东西证明cayley要证明一一对应的关系(双射)
考虑如何构造,发现当前位置的prufer序列是最小的叶子节点,我们把叶子节点都先丢进一个堆,然后每次取堆首的时候顺便把他的父亲放进堆里面,时间复杂度O(nlogn)事实证明本题的瓶颈还是在读入上面,所以把读入整好了也是能过的(
| 12
 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;
 }
 
 | 
考虑还原这棵树,不难发现不在prufer序列上面的点一定是叶子节点(度为1),我们每次就找到最小的叶子节点,然后就可以根据prufer序列来还原(得到父亲)
这样时间复杂度太高了,实际上我们每次找第一个的时候就只有两种情况,第一个更小和更大,更大的话很好做,直接用一个指针扫,变小就直接一个while循环到变大或者结束,时间复杂度是O(N)
| 12
 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(){
 
 int lok=1;
 int i;
 for(i=1;i<=n-2;i++) d[a[i]]++;
 a[n-1]=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;
 }
 
 
 | 
然后接着想你就会发现,这东西不仅可以用来做prufer还原f,同理还可以用来构造prufer
| 12
 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];
 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
 
 |