图论板子
最短路
floyed(加最小环)
void floyed(){
//注意初始化加赋值
for(int k=1;k<=n;++k){
for(int i=1;i<k;++i){
for(int j=i+1;j<k;++j){
ans=min(ans,dis[i][j]+a[i][k]+a[k][j]);
}
}
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]) ;
}
}
}
}
SPFA(加判断负环)
void spfa(int s){
for(int i=1;i<=n;++i) dis[i]=INF;
dis[s]=0;
q.push(s);
while(!q.empty()){
int u=q.front();
vis[u]=0;q.pop();
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(dis[v]>dis[u]+e[i].w){
dis[v]=dis[u]+e[i].w;
pre[v]=u;//记录路径用
if(!vis[v]){
q.push(v);
vis[v]=1;
/*in[v]++
if(in[v]>n){
//如果一个顶点的入队个数大于n,即有负环
return true;
}*/
}
}
}
}
}
堆优化dijkstra
struct node{
int w,s;
node(){}
node(int a,int b){
s=a;
w=b;
}
bool operator<(const node &a)const{
return w>a.w;
//小根堆
}
};
void dijkstra(int s){
for(int i=1;i<=n;++i) dis[i]=INF;
dis[s]=0;
q.push(node(s,0));
while(!q.empty()){
int u=q.top().s;
if(vis[u]) continue;
vis[u]=1;q.pop();
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(dis[v]>dis[u]+e[i].w){
dis[v]=dis[u]+e[i].w;
q.push(node(v,dis[v]));
}
/*
if(dis[v]>max(e[i].w,dis[u])){
dis[v]=max(e[i].w,dis[u]);
求最短路径最大值
}
*/
}
}
}
欧拉欧拉欧拉欧拉(欧拉回路)
void dfs(int t){
for(int i=1;i<=n;++i){
if(a[t][i]){
a[t][u]--;a[i][t]--;
dfs(i);
}
}
pr[++pr[0]]=t;
//注意倒序输出
}
void oula(){
for(int i=1;i<=m;++i){
a[x][y]++;a[y][x]++;
du[x]++;du[y]++;
//存边
}
int st=1;
for(int i=1;i<=n;++i){
if(du[i]&1){
st=i;
break;
}
//找奇点
}
dfs(st);
}
最小生成树
kruslal
struct node{
int x,y,w;
node(){}
node(int a,int b,int c):x(a),y(b),w(c){}
bool operator <(const node &c)const{
return w<c.w;
}
}e[maxn];
int kruskal(){
int mst=0,k=0;
for(int i=1;i<=n;++i) f[i]=i;
//并查集初始化
sort(e+1,e+1+m);
for(int i=1;i<=m;++i){
if(find(e[i].x)!=find(e[i].y)){
merge(e[i].x,e[i].y);
mst+=e[i].w;
k++;
if(k==n-1){
//构成最小树
break;
}
}
}
return mst;
}
最小差值生成树
#include<bits/stdc++.h>
#define R register int
using namespace std;
int n,m;
int f[1000001];
int cnt;
struct node{
int x,y,w;
node(){}
node(int a,int b,int c):x(a),y(b),w(c){}
bool operator <(const node &c)const{
return w<c.w;
}
}kar[1000001];
int find(int x){
if(f[x]!=x) f[x]=find(f[x]);
return f[x];
}
void merge(int x,int y){
int rx=find(x);
int ry=find(y);
f[ry]=rx;
}
void pre(){
for(R i=1;i<=m;++i) f[i]=i;
}
int mst,ans=999999999;
void kruskal(){
mst=0;
int k=0;
sort(kar+1,kar+1+m);
for(R j=1;j<=m;++j){
k=0;pre();//初始化
for(R i=j;i<=m;++i){
//从最小值开始枚举
if(find(kar[i].x)!=find(kar[i].y)){
merge(kar[i].x,kar[i].y);
k++;
if(k==n-1){
ans=min(ans,kar[i].w-kar[j].w);
//最大值-最小值
break;
}
}
}
if(k<n-1||ans==0){
//某种优化
break;
}
}
}
int main(){
scanf("%d%d",&n,&m);
int x,y,w;
for(R i=1;i<=m;++i){
scanf("%d %d %d",&x,&y,&w);
if(x==y){
//无用边
continue;
}else{
kar[i].x=x;
kar[i].y=y;
kar[i].w=w;
}
}
kruskal();
if(ans==9999999) cout<<-1;
else cout<<ans;
return 0;
}
LCA
树上倍增:-)
dfs(1,0,1)//调用入口
void dfs(int u,int fa,int dep){
re[u]=dep;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v==fa)continue;
f[v][0]=u;
dfs(v,u,dep+1);
}
//建立父子关系
}
void pre(){
for(int j=1;(1<<j)<=n;++j){
for(int i=1;i<=n;++i){
f[i][j]=f[f[i][j-1]][j-1];
}
}
}
int lca(int x,int y){
if(re[x]<re[y]) swap(x,y);
for(int i=20;i>=0;i--){
if(re[f[x][i]]>=re[y]) x=f[x][i];
}
if(x==y) return x;
for(int i=20;i>=0;i--){
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
}
return f[x][0];
}
最小瓶颈生成树
最小生成树的最大边,反之同理
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=10005;
int head[maxn],n,m,R,cnt;
int fa[maxn],f[maxn][51];
int minn[maxn][51];
bool za[maxn];
int re[maxn<<1];
struct bian{
int x,y,w;
bool operator <(const bian &a)const{
return w>a.w;
}
}e2[maxn];
struct ed{
int next,to,w;
}e[maxn<<1];
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=s*10+ch-'0';
ch=getchar();
}
return s*w;
}
void add(int u,int v,int w){
e[++cnt].to=v;
e[cnt].next=head[u];
e[cnt].w=w;
head[u]=cnt;
}
int root=1;
int find(int x){
if(fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
void bing(int x,int y){
int rx=find(x);
int ry=find(y);
fa[rx]=ry;
}
void kruskal(){
for(register int i=1;i<=n;++i) fa[i]=i;
int k=0;
for(register int i=1;i<=m;++i){
if(find(e2[i].x)!=find(e2[i].y)){
bing(e2[i].x,e2[i].y);
add(e2[i].x,e2[i].y,e2[i].w);
add(e2[i].y,e2[i].x,e2[i].w);
root=e2[i].x;
k++;
if(k==n-1){
return;
}
}
}
}
int lca(int x,int y){
if(re[x]<re[y]) swap(x,y);
int tot=99999999;
for(register int j=20;j>=0;j--){
if(re[x]-(1<<j)>=re[y]){
tot=min(tot,minn[x][j]);
x=f[x][j];
}
if(x==y) return tot;
//注意这里直接返回
}
for(register int j=20;j>=0;j--){
if(re[x]>=(1<<j)&&f[x][j]!=f[y][j]){
tot=min(tot,min(minn[x][j],minn[y][j]));
x=f[x][j];
y=f[y][j];
}
}
tot=min(tot,minn[x][0]);
tot=min(tot,minn[y][0]);
return tot;
}
void dfs(int u,int fa,int dep){
re[u]=dep;
for(register int i=head[u];i;i=e[i].next){
int v=e[i].to,w=e[i].w;
if(fa!=v){
f[v][0]=u;
minn[v][0]=w;
dfs(v,u,dep+1);
}
}
}
int main(){
int q=0;
n=read();m=read();
for(register int i=1;i<=m;++i){
int x,y,w;
x=read();y=read();w=read();
e2[i].x=x;
e2[i].y=y;
e2[i].w=w;
}
sort(e2+1,e2+1+m);
kruskal();
q=read();
memset(minn,0x7f,sizeof(minn));
dfs(root,0,1);
for(register int j=1;(1<<j)<=n;++j){
for(register int i=1;i<=n;++i){
f[i][j]=f[f[i][j-1]][j-1];
minn[i][j]=min(minn[i][j-1],minn[f[i][j-1]][j-1]);
}
}
for(register int i=1;i<=q;++i){
int x,y;
x=read();y=read();
if(find(x)!=find(y)){
printf("-1\n");
continue;
}
int z=lca(x,y);
printf("%d\n",z);
}
return 0;
}
dfs序
void dfs(int u,int fa){
tot++;
in[u]=tot;
for(register int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v!=fa) {
dfs(v,u);
//out[u]=++tot; 欧拉1
}
}
out[u]=tot;
//out[u]=++tot; 欧拉2
}
tarjan
强♂联通分量
void tarjan(int u){
tot++;vis[u]=1;s[++top]=u;
dfn[u]=low[u]=tot;
for(register int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(!dfn[v]) {
tarjan(v,u);
low[u]=min(low[u],low[v]);
}else if(vis[v]){
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u]){
int y;z++;
do{
y=s[top--];
vis[y]=0;
belong[y]=z;
}while(y!=u);
}
}
割点
void tarjan(int u,int fa){
tot++;
dfn[u]=low[u]=tot;
int son=0;bool F=1;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v==fa&&F){
F=0;continue;
}
if(!dfn[v]) {
tarjan(v,u);son++;
low[u]=min(low[u],low[v]);
if(dfn[u]<=low[v]){
//割点存在
if(u!=root||son>1){
dot[u]=1;
}
}
}else{
low[u]=min(low[u],dfn[v]);
}
}
}
点双联通分量
void tarjan(int u,int fa){
tot++;s[++top]=u;
dfn[u]=low[u]=tot;
int son=0;bool F=1;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v==fa&&F){
F=0;continue;
}
if(!dfn[v]) {
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(dfn[u]<=low[v]){
//割点存在
cut[u]=1;
dslt[cnt].push_back(u);
int y=0;cnt++;
do{
y=s[top--];
dslt[cnt].push_back(y);
}while(y!=v);
}
}else low[u]=min(low[u],dfn[v]);
}
if(fa==0&&son==1) cut[u]=0;
}
割边
void tarjan(int u){
tot++;
dfn[u]=low[u]=tot;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(i==p[u]^1) continue;
if(!dfn[v]) {
p[v]=i;//记录编号
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(dfn[u]<low[v]){
//割边存在
cnt++;
cut[i]=cut[i^1]=1;
}
}else low[u]=min(low[u],dfn[v]);
}
}
边双联通分量
//存边时下标务必从0存储
void tarjan(int u,int eid){
tot++;vis[u]=1;s[++top]=u;
dfn[u]=low[u]=tot;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;int id=e[i].id;
if(id==eid^1) continue;
if(!dfn[v]) {
tarjan(v,id);
low[u]=min(low[u],low[v]);
}else low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
cut[eid]=1;
cnt++;
int x;
do{
x=s[top--];
bslt[cnt].push_back(x);
}while(x!=u);
}
}
矿场搭建(割点做法)
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=600;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=s*10+ch-'0';
ch=getchar();
}
return s*w;
}
int head[maxn],cnt,n,tot,dfn[maxn],low[maxn];
int dl[maxn],vis[maxn],num,cut;
int root=1;
struct ED{
int to,next;
}e[maxn<<1];
ll solu,total;
bool zio[maxn];
void add(int u,int v){
e[++cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
stack<int> s;
void tarjan1(int u){
++tot;
dfn[u]=tot;
low[u]=tot;
int wenqizhi=0;
for(register int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(!dfn[v]){
wenqizhi++;
tarjan1(v);
low[u]=min(low[u],low[v]);
if(dfn[u]<=low[v]){
if(u!=root||wenqizhi>1){
zio[u]=1;
}
}
}else{
low[u]=min(low[u],dfn[v]);
}
}
}
void dfs(int u,int fa){
vis[u]=fa;
num++;
for(register int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(vis[v]!=fa&&zio[v]){
cut++;
vis[v]=fa;
}
if(vis[v]) continue;
dfs(v,fa);
}
tot=max(tot,dl[u]);
}
int main(){
int p=0;
while(1){
p++;
cnt=0;tot=0;
total=0;solu=1;
memset(head,0,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(zio,0,sizeof(zio));
memset(vis,0,sizeof(vis));
memset(dl,0,sizeof(dl));
memset(e,0,sizeof(struct ED)*maxn);
n=read();
if(n==0) break;
int nn=0;
for(register int i=1;i<=n;++i){
int x,y;x=read();y=read();
add(x,y);add(y,x);
nn=max(nn,max(x,y));
}
for(register int i=1;i<=nn;++i){
if(!dfn[i]) {
root=i;
tarjan1(i);
}
}
int opt=0;
for(register int i=1;i<=nn;++i){
if(!zio[i]&&!vis[i]) {
num=0;
cut=0;
dfs(i,++opt);
if(cut==0){
total+=2;
solu*=(long long)num*(num-1)/2;
}
if(cut==1){
total++;
solu*=(long long)num;
}
}
}
printf("Case %d: %lld %lld\n",p,total,solu);
}
return 0;
}
//通过查询每个连通块的割点数量来求值
未完。

浙公网安备 33010602011771号