图论板子
好多板子都没打啊...
快读
哪都说不定要用的对不...
inline int read()
{
int x=0; bool flag=1; char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
flag=0;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+ch-'0';
// x=(x<<1)%mod+(x<<3)%mod+ch-'0'; 就是带模快读
ch=getchar();
}
if(flag)
return x;//x%mod
return -x;//-x%mod
}
最短路
dij
struct node
{
int link;
long long w;
bool operator < (const node &qq) const{
return qq.w<w;
}
};
vector <node> f[N];
priority_queue <node> q;
void dij(int s)
{
while(!q.empty())
q.pop();
for(int i=1;i<=n;i++)
dis[i]=inf;
dis[s]=0;
q.push((node){s,0});
while(!q.empty())
{
node temp=q.top();
q.pop();
if(use[temp.link])
continue;
use[temp.link] = 1;
for(auto i:f[temp.link])
{
if(dis[i.link]>=dis[temp.link]+i.w)
{
dis[i.link] = dis[temp.link] + i.w;
q.push((node){i.link, dis[i.link]});
}
}
}
}
spfa
void spfa(int s)
{
memset(use,0,sizeof(use));
memset(dis,0x7f,sizeof(dis));
use[s]=1;
dis[s]=0;
q.push((node){s,0});
while(!q.empty())
{
node temp=q.front();
q.pop();
use[temp.link]=0;
for(int i=0;i<f[temp.link].size();i++)
{
node xx=f[temp.link][i];
if(dis[xx.link]>dis[temp.link]+xx.w)
{
dis[xx.link]=dis[temp.link]+xx.w;
if(!use[xx.link])
{
q.push((node){xx.link,dis[xx.link]});
use[xx.link]=1;
}
}
}
}
}
spfa可以跑负权,但是dijstar不行,都可以跑最长路找环(除了跑有负权的边以外别用spfa了)
拓扑排序
void top()
{
while(!q.empty())
{
int temp=q.front();
q.pop();
for(auto i:f[temp])
{
d[i]--;
/*code*/
if(d[i]==0)
q.push(i);
}
}
}
拓扑排序也可以找最短路|最长路(有人又花里胡哨把这叫dp)就在code里加dis[i]=min(...)就完事了
和dijkstar本质就是只能DAG里面找吧,并且只能从入度为0的点为起点,优点就是可以同时找多条起点和终点的dis(从这个答案角度来说,叫dp似乎也没问题)虽然dijkstar建超级起点和汇点也行比较拓扑本职工作也不是干这的,别要求太多了
在有向无环图中可以拓扑排序来找到一个拓扑序列,拓扑排序也可以找环
由于拓扑排序每次都是从入度为0的点开始,而环上的点的入度都不会为0,所以环上的点就不会参加排序,也就是说,经过拓扑排序后剩下的边和点构成的都是环(入读不为零的点)
倍增LCA
fa[N][30],deep[N]
vector <int> f[N];
void dfs(int x)
{
for(int i=1;i<=25;i++)
{
if(deep[x]<(1<<i))
break;
fa[x][i]=fa[fa[x][i-1]][i-1];
}
for(int i=0;i<f[x].size();i++)
{
int temp=f[x][i];
if(temp!=fa[x][0])
{
fa[temp][0]=x;
deep[temp]=deep[x]+1;
dfs(temp);
}
}
}
int lca(int x,int y)
{
if(deep[x]<deep[y])
swap(x,y);
int t=deep[x]-deep[y];
for(int i=0;i<=25;i++)
{
if(t&(1<<i))
x=fa[x][i];
}
if(x==y)
return x;
for(int i=25;i>=0;i--)
{
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];
}
并查集
也可以找环....tarjan也是用并查集来维护找出的强连通分量(能找环的太多了...)
int fa[N],ha[N],cnt;
long long w[N],sum[N];
int find(int x)
{
//if(x==fa[x])
// return x;
//return fa[x]=find(fa[x]);
// 最后return 应该是return find(fa[x]) 不然有些题是过不了的(还是说本来就是错误的)
return fa[x]==x?fa[x]:find(fa[x]);//等价该过的写法(我居然三年都没发现问题!!!)
}
void merge(int a,int b)
{
int r1=find(ha[a]),r2=find(fa[ha[b]]);
if(r1!=r2)
{
fa[r1]=r2;
w[r2]+=w[r1];
sum[r2]+=sum[r1];
}
}
void move(int a,int b)
{
// 把a移动到b集合
int r1=find(ha[a]),r2=find(ha[b]);
if(r1!=r2)
{
w[r1]-=a;
sum[r1]-=1;
w[r2]+=a;
sum[r2]+=1;
ha[a]=++cnt; //先把a移出再定向father
// 虽然之前的ha[a]还在集合里,但是之后再也不会用到,并且权值该减掉的全部减掉了所以相当于删除操作
fa[ha[a]]=r2;
}
}
int main()
强连通
缩点
void tarjan(int x)
{
dfn[x]=low[x]=++tot;
use[x]=1;
s.push(x);
for(int i=0;i<f[x].size();i++)
{
int temp=f[x][i];
if(use[temp]==0)
{
tarjan(temp);
low[x]=min(low[x],low[temp]);
}
else
{
if(use[temp]==1)
low[x]=min(low[x],dfn[temp]);
}
}
if(low[x]==dfn[x])//类似并查集合并
{
sum++;
while(!s.empty())
{
int temp=s.top();
s.pop();
use[temp]=0;
be[temp]=sum;//sum是最后缩点后的点个数
sumv[sum]+=w[temp];//权值该干嘛干嘛
if(x==temp)
return ;
}
}
}
割点
int tim,dfn[N],low[N];
bool use[N];//是否是割点
vector<int> f[N];
void tarjan(int x,int fa)//求割点
{
dfn[x] = low[x] = ++tim;
int child = 0;
for (auto i:f[x])
{
if(dfn[i]==0)
{
tarjan(i,fa);
low[x] = min(low[x], low[i]);
if (low[i]>=dfn[x]&&x!=fa)//非根节点并且子节点low都大于dfn[x]说明是割点(
use[x] = 1;
if(x==fa)
child++;
}
low[x] = min(low[x], dfn[i]);//这里与缩点不同
}
if(child>=2&&x==fa)//根节点且有两个子树为割点
{
use[x]=1;
}
}
网络流
普通的网络流算法
dinic
#include<bits/stdc++.h>
using namespace std;
const int N = 5e3+5,inf=0x3f3f3f3f;
int n, m, s, t, x, y, z;
int level[N];
struct node
{
int link;
int w;
int position;
};
vector<node> f[N];
queue<int> q;
int dfs(int x,int flow)
{
int ans = 0;
if(x==zx||flow==0)
return flow;
for(auto &i:f[x])//结构体无法在外部改变,外面只是创建了一个临时变量(相当结构体内部变量是私有属性)
{
if(i.w>0&&level[i.link]==level[x]+1)// 保证边权不为0 且按照深度dfs
{
int d = dfs(i.link, min(flow, i.w)); // 搜索,flow是最小的所以是min
if(d==0)
level[i.link] = 0;
if(d>0)
{
i.w -= d;//正向边减少
f[i.link][i.position].w += d;// 反向边增加
flow -= d;
ans += d;
if(flow==0)
break;
}
}
}
return ans;
}
bool bfs()
{
q.push(s);
memset(level, -1, sizeof(level));
level[qx] = 0;
while(!q.empty())
{
int temp = q.front();
q.pop();
for(auto &i:f[temp])
{
if(i.w==0||level[i.link]!=-1)
continue;
level[i.link] = level[temp] + 1;
q.push(i.link);
}
}
if(level[zx]!=-1)
return 1;
return 0;
}
long long dinic()
{
long long ans = 0;
while(bfs())
ans += dfs(qx, inf);
return ans;
}
int main()
{
scanf("%d %d %d %d", &n,&m,&qx,&zx);
for (int i = 1; i <= m;i++)
{
scanf("%d %d %d", &x, &y, &z);
f[x].push_back((node){y, z,(int)f[y].size()});
f[y].push_back((node){x, 0,(int)f[x].size()-1});// 获取x在y的第几条边
}
printf("%lld\n", dinic());
return 0;
}
有源汇的上下界网络流
有源汇就是比无源汇多加了一个超超级源点和超超级汇点
最大流:可行流+(删点后的)qx->zx 的最大流
最小流:可行流-(删点后的)zx->qx 的最大流
#include<bits/stdc++.h>
using namespace std;
const int N = 10003,inf=0x3f3f3f3f;
int qx,zx,n, m, k,x,y,a[N][N],in[N];
int maxflow, mcost,ans,sum,level[N];
struct node
{
int link, w, position;
};
vector<node> f[N];
queue<int> q;
void add_edge(int x,int y,int w)
{
f[x].push_back((node){y, w, f[y].size()});
f[y].push_back((node){x, 0, f[x].size() - 1});
}
int dfs(int x,int flow)
{
int ans = 0;
if(x==zx||flow==0)
return flow;
for(auto &i:f[x])//结构体无法在外部改变,外面只是创建了一个临时变量(相当结构体内部变量是私有属性)
{
if(i.w>0&&level[i.link]==level[x]+1)// 保证边权不为0 且按照深度dfs
{
int d = dfs(i.link, min(flow, i.w)); // 搜索,flow是最小的所以是min
if(d==0)
level[i.link] = 0;
if(d>0)
{
i.w -= d;//正向边减少
f[i.link][i.position].w += d;// 反向边增加
flow -= d;
ans += d;
if(flow==0)
break;
}
}
}
return ans;
}
bool bfs()
{
q.push(qx);
memset(level, -1, sizeof(level));
level[qx] = 0;
while(!q.empty())
{
int temp = q.front();
q.pop();
for(auto &i:f[temp])
{
if(i.w==0||level[i.link]!=-1)
continue;
level[i.link] = level[temp] + 1;
q.push(i.link);
}
}
if(level[zx]!=-1)
return 1;
return 0;
}
int dinic()
{
int ans = 0;
while(bfs())
ans += dfs(qx, inf);
return ans;
}
int main()
{
scanf("%d %d %d", &n, &m,&k);
qx = n+m+2;
zx = n+m+3;
for (int i = 1; i <= n;i++)
{
scanf("%d", &x);
add_edge(0, i, inf-x);
in[i] += x;
in[0] -= x;
}
for (int i = 1; i <= m;i++)
{
scanf("%d", &x);
add_edge(i+n, n+m+1, inf-x);
in[i + n] -= x;
in[n + m + 1] += x;
}
for (int i = 1; i <= k;i++)
{
scanf("%d %d", &x, &y);
a[x][y+n] = 1;
}
for (int i = 1; i <= n;i++)
{
for (int j = 1 + n; j <= n + m;j++)
{
if(a[i][j]==1)
continue;
add_edge(i, j, 1);
}
}
for (int i = 0; i <= n + m+1;i++)
{
if(in[i]>0)
{
add_edge(qx, i, in[i]);
sum += in[i];
}
else
{
add_edge(i, zx, -in[i]);
}
}
add_edge(n+m+1, 0, inf);
maxflow=dinic();
//通过满流判定是否有可行流
if(maxflow!=sum)
{
printf("JIONG\n");
}
else
{
//t->s的反向边永远都是可行流
maxflow=f[0][f[0].size()-1].w;
//删边
f[qx].clear();
f[zx].clear();
for (int i = 0; i <= n + m + 1;i++)
{
f[i].pop_back();
}
//求最小流就是maxflow-反向遍历的最大流
//求最大流就是maxflow+正向遍历的最大流
qx = n + m + 1;
zx = 0;
printf("%d\n",maxflow - dinic());
}
return 0;
}
费用流
是不是应该和网络流放到一个分类里
ek+spfa (\(nm^2\))
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4+5,inf=0x3f3f3f3f;
int n, m, s, t, a,b,c,d;
int pre[N],dis[N],flow[N],pos[N];
bool use[N];
long long mincost, maxflow;
struct node
{
int link;
int w;
int cost;
int position;
};
vector<node> f[N];
queue<int> q;
bool spfa()
{
while(!q.empty())
q.pop();
for (int i = 1; i <= n;i++)
{
dis[i] = inf;
use[i] = 0;
pre[i] = -1;
}
use[qx] = 1;
flow[qx] = inf;
dis[qx] = 0;
pre[qx] = 0;
q.push(qx);
while(!q.empty())
{
int temp = q.front();
q.pop();
use[temp] = 0;
for (int i = 0; i < f[temp].size();i++)
{
node xx = f[temp][i];
if(xx.w>0&&dis[xx.link]>dis[temp]+xx.cost)
{
dis[xx.link] = dis[temp] + xx.cost;
pre[xx.link] = temp;
flow[xx.link] = min(flow[temp],xx.w);
pos[xx.link] = i;//记录这个点是temp的第几条边
if(use[xx.link]==0)
{
use[xx.link] = 1;
q.push(xx.link);
}
}
}
}
// for (int i = 1; i <= n;i++)
// printf("i=%d %d\n", i,dis[i]);
// printf("pret=%d\n", pre[zx]);
if (pre[zx] == -1)
return 0;
return 1;
}
void update()
{
int now = zx;
while(now!=qx)
{
node &temp = f[pre[now]][pos[now]];
temp.w -= flow[zx];
node &temp2=f[now][temp.position];
temp2.w+= flow[zx];
now = pre[now];
}
mincost += flow[zx] * dis[zx];
maxflow += flow[zx];
}
long long mfmc()
{
while(spfa())
{
update();
}
}
int main()
{
scanf("%d %d %d %d", &n,&m,&qx,&zx);
for (int i = 1; i <= m;i++)
{
scanf("%d %d %d %d", &a, &b, &c,&d);
f[a].push_back((node){b, c,d,(int)f[b].size()});
f[b].push_back((node){a, 0,-d,(int)f[a].size()-1});// 注意翻遍花费是相反数
}
mfmc();
printf("%lld %lld\n",maxflow, mincost);
return 0;
}
dinic+spfa 费用流 (\(n^2m\))
#include<bits/stdc++.h>
using namespace std;
const int N = 5006,inf = 1e9;
int T,n, m, qx,zx,a,b,c,d,e,r[N];
bool use[N];
long long mcost,dis[N],maxflow;
// pre记录回溯前驱,dis是 cost*w ,pre取代了level数组
struct node
{
int link;
int w;
int cost;
int position;
};
vector<node> f[N];
deque<int> q;
void add_edge(int u,int v,int w,int cost)
{
//建立u->v的正向边
f[u].push_back((node){v, w,cost,(int)f[v].size()});
f[v].push_back((node){u, 0,-cost,(int)f[u].size()-1});
// 注意花费是相反数
}
bool spfa()//最短路
{
for (int i = 0; i <= n; i++)
{
dis[i] = inf;
use[i] = 0;
}
use[zx] = 1;
dis[zx] = 0;
while(!q.empty())
{
q.pop_back();
}
q.push_back(zx);
while(!q.empty())
{
int temp = q.front();
q.pop_front();
use[temp] = 0;
for (int i = 0; i < f[temp].size();i++)
{
node xx = f[temp][i];
// 因为spfa是倒着跑所以要保证正流量应该是反边大于0
if(f[xx.link][xx.position].w&&dis[xx.link]>dis[temp]-xx.cost)
{
dis[xx.link] = dis[temp] - xx.cost;
if(use[xx.link]==0)
{
use[xx.link] = 1;
q.push_back(xx.link);
}
}
}
}
return dis[qx] < inf;
}
long long dfs(int x,long long flow)
{
use[x] = 1;
long long ans = 0;
if(x==zx||flow==0)
return flow;
for(auto &i:f[x])//结构体无法在外部改变,外面只是创建了一个临时变量(相当结构体内部变量是私有属性)
{
if(i.w>0&&dis[i.link]==dis[x]-i.cost&&use[i.link]==0)// 保证边权不为0 且按照深度dfs
{
long long d = dfs(i.link, min(flow,(long long) i.w)); // 搜索,flow是最小的所以是min
if(d>0)
{
i.w -= d;//正向边减少
f[i.link][i.position].w += d;// 反向边增加
flow -= d;
ans += d;
mcost += d * i.cost;//多了个这个统计mcost
if(flow==0)
break;
}
}
}
return ans;
}
void mfmc()
{
while(spfa())
{
use[zx] = 1;
while(use[zx])
{
memset(use, 0, sizeof(use));
maxflow+=dfs(qx,inf);
}
}
}
int main()
{
scanf("%d %d %d %d", &n,&m,&qx,&zx);
for (int i = 1; i <= m;i++)
{
scanf("%d %d %d %d", &a, &b, &c,&d);
add_edge(a, b, c, d);
}
mfmc();
printf("%lld %lld\n",maxflow, mcost);
return 0;
}
高精
搞烦了实在不行换python
似乎和图论没啥关系,加都加了算了
string add(string str1,string str2)
{
string str;
int len1=str1.length();
int len2=str2.length();
//前面补0,弄成长度相同
if(len1<len2)
{
for(int i=1;i<=len2-len1;i++)
str1="0"+str1;
}
else
{
for(int i=1;i<=len1-len2;i++)
str2="0"+str2;
}
len1=str1.length();
int cf=0;
int temp;
for(int i=len1-1;i>=0;i--)
{
temp=str1[i]-'0'+str2[i]-'0'+cf;
cf=temp/10;
temp%=10;
str=char(temp+'0')+str;
}
if(cf!=0) str=char(cf+'0')+str;
return str;
}
$道路千万条,点赞第一条;阅读不规范,笔者两行泪$