qbzt day4 下午

有向图的强连通分量

强联通:两个点之间可以互相到达

如果某个图任意两个点都是强联通的,那么称这个图强联通

如果一个图的子图是强联通的,那么称这个图是强联通子图

一个图的极大强联通子图被称作强连通分量

有强联通分量意味着环

 

例:受欢迎的牛

如果有环,意味着这个环里的牛都互相喜欢

我们可以先求出环,然后把每一个环都看作一个点,这样整个图就变成了一个DAG(有向无环图)

看有几个点出度为0,如果大于一个点没有出边,就说明没有最受欢迎的牛

如果只有一个,那么强联通分量的大小就是答案

void tarjan(int u){
    dfn[u]=++ind;
    low[u]=dfn[u];
    s[top++]=u;
    in[u]=1;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(dfn[v]==0){//mei bian li dao, v zai zi shu li mian
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }else{//bian li dao le, v bu zai zi shu li mian
            if(in[v]){//zai zhan li mian
                low[u]=min(low[u],dfn[v]);
            }
        }
    }
    if(dfn[u]==low[u]){//fa xian scc
        cnt_scc++;
        while(s[top]!=u){//bu duan chu zhan
            top--;
            in[s[top]]=0;
            scc[s[top]]=cnt_scc;
        }
    }
}

 完整代码:

#include<bits/stdc++.h>
using namespace std;

int n,m;

int cnt,head[50000];

struct edge
{
    int to,nxt;
}edg[50005];

inline void add(int from,int to)
{
    edg[++cnt].to=to;
    edg[cnt].nxt=head[from];
    head[from]=cnt;
}

int dfn[50005],low[50005],ind,in[50005];
int s[50005],top;
int cnt_scc;
int scc[50005],cntscc[50005];

void tarjan(int x)
{
    dfn[x]=++ind;
    low[x]=dfn[x];
    s[top++]=x;
    in[x]=1;
    for(int i=head[x];i;i=edg[i].nxt)
    {
        int v=edg[i].to;
        if(!dfn[v])
        {
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else
        {
            if(in[v])
            {
                low[x]=min(low[x],dfn[v]);
            }
        }
    }
    if(dfn[x]==low[x])
    {
        cnt_scc++;
        while(s[top]!=x)
        {
            top--;
            in[s[top]]=0;
            scc[s[top]]=cnt_scc;
            cntscc[cnt_scc]++;
        }
    }
}

int out[50005];
int ans;

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,x,y;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
    }
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i]) tarjan(i);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=head[i];j;j=edg[j].nxt)
        {
            int k=edg[j].to;
            if(scc[i]!=scc[k]) out[scc[i]]++;
        }
    }
    for(int i=1;i<=cnt_scc;i++)
    {
        if(!out[i]) 
        {
            if(!ans)
                ans=i;
            else
            {
                cout<<0;
                return 0;
            }
        }
    }
    cout<<cntscc[ans];
}

 

求强联通分量:

首先想到dfs,我们不妨先看看dfs数

发现有向图中dfs树会有横叉边

显然的事实:一个强连通分量一定是dfs树上的连续的一块

我们定义两个数组

dfn[x]表示x是第几个被dfs到的数(时间戳)

low[x]表示当前节点以及他的子树所有出边所能连到的dfn值中最小的一个

 

如果某一个点的low和他的dfn相同,就意味着出现强联通分量,就把这个强连通分量拿去(没了)

用一个栈来实现,寻找low时只在栈里面找,弹出时不断从栈顶弹出直到弹出这个点

 

A gift

把g按照升序排序,枚举g0,跑最小生成树(然后就tle)O(m^2logm)

当我们再连一条新边时,会出现环,所以找到环上的最大的边然后删掉它,复杂度O(n)

总复杂度O(mn)

 

狼抓兔子

平面图的最小割等于对偶图的最短路

最小割:所有割中边权和最小的割

对偶图:边变成点,点变成边    平面图的每一个块变成一个点,每一条边变成垂直的一条边

#include<cstdio>
#include<cstring>
#define oo 0x3f
#define MAXN 2000001
using namespace std;
struct edge {
    int v,to,next;
} e[MAXN*2];
int dis[MAXN],q[MAXN],head[MAXN];
bool tag[MAXN];
int n,m,ne,x;
void insert(int u,int v,int w) {
    ne++;
    e[ne].to=v;
    e[ne].next=head[u];
    e[ne].v=w;
    head[u]=ne;
}
void spfa() {
    memset(dis,oo,sizeof(dis));
    int t=0,w=1;
    tag[0]=1;
    q[w]=0;
    dis[0]=0;
    while(t!=w) {
        int u=q[t++];
        tag[u]=0;
        if(t==MAXN)    t=0;
        for(int i=head[u]; i; i=e[i].next) {
            int v=e[i].to;
            if(dis[v]>dis[u]+e[i].v) {
                dis[v]=dis[u]+e[i].v;
                if(tag[v]==0) {
                    q[w++]=v;
                    tag[v]=1;
                    if(w==MAXN)    w=0;
                }
            }
        }
    }
}
int main() {
    scanf("%d%d",&n,&m);
    int nm=(n*m-n-m+1)<<1;
    for(int i=1; i<=n; i++) {
        for(int j=1; j<m; j++) {
            scanf("%d",&x);
            if(i==1)    insert(j,nm+1,x);
            else if(i==n)    insert(0,(((n-1)<<1)-1)*(m-1)+j,x);
            else    insert(((i-1)<<1)*(m-1)+j,(((i-1)<<1)-1)*(m-1)+j,x);
        }
    }
    for(int i=1; i<n; i++) {
        for(int j=1; j<=m; j++) {
            scanf("%d",&x);
            if(j==1)    insert(0,((i<<1)-1)*(m-1)+1,x);
            else if(j==m)    insert(((i<<1)-1)*(m-1),nm+1,x);
            else    insert(((i-1)<<1)*(m-1)+j-1,((i<<1)-1)*(m-1)+j,x);
        }
    }
    for(int i=1; i<n; i++) {
        for(int j=1; j<m; j++) {
            scanf("%d",&x);
            insert((((i-1)<<1)+1)*(m-1)+j,((i-1)<<1)*(m-1)+j,x);
        }
    }
    spfa();
    printf("%d
",dis[nm+1]);
    return 0;
}

 

奶牛的旅行

0/1分数规划

二分答案+spfa判负环

 

最优比率生成树

 

抢掠计划

把强联通分量缩点

从点1开始跑一遍最长路spfa,在酒吧取max

 

奶牛接力跑

倍增floyd(floyd快速幂)

g[1][i][j]表示从i到j只经过一条边的最短路

如何转移到g[2][i][j]?

枚 举所有中点k,对于所有的k,g[2][i][j]表示min(g[1][i][k]+g[1][k][j])

同理也可以求出g[4][i][k]

所以g[p][i][j]=min(g[p/2][i][k]+g[p/2][k][j])

g[3][i][j]=min(g[2][i][k]+g[1][k][j])

和快速幂的关系:用类似于快速幂的方法将其分解

while(b){
    if(b&1){
        memset(f,0x3f,sizeof(f));
        for(int k=1;k<=n;k++){
            for(int i=1;i<=n;i++){
                for(int j=1;j<=n;j++){
                    f[i][j]=min(f[i][j],ret[i][k]+g[k][j]);
                }
            }
        }
        memcpy(ret,f,sizeof(f));
    }
    memset(f,0x3f,sizeof(f));
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                f[i][j]=min(f[i][j],g[i][k]+g[k][j]);
            }
        }
    }
    memcpy(g,f,sizeof(f));
   b>>=1; } print(ret[S][E])

 

Destroying road

  1. 两条最短路不交叉  删掉的边的数量:m-dis[s1][t1]-dis[s1][t2]
  2. 两条最短路有公共部分

Bfs求最短路

 

匈牙利算法:配对

int g[N][N];
int lk[N];// mei zi xi huan na ge nan de
bool vis[N];//zhe yi lun, mei zi you mei you bei jiao huan

bool find(int x){
    for(int i=1;i<=n;i++){
        if(!vis[i]&&g[x][i]){
            vis[i]=1;
            if(lk[i]==0||find(lk[i])){
                lk[i]=x;
                return 1;
            }
        }
    }
    return 0;
}

for(int i=1;i<=n;i++){
    memset(vis,0,sizeof(vis));
    if(find(i)){
        hunpei++;
    }else{
        break;
    }
}

差分约束系统

最小化最长路

例题:糖果

原文地址:https://www.cnblogs.com/lcezych/p/11196583.html