图论板子

好多板子都没打啊...

快读

哪都说不定要用的对不...

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==t||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[s] = 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[t]!=-1)
        return 1;
    return 0;
}
long long dinic()
{
    long long ans = 0;
    while(bfs())
        ans += dfs(s, inf);
    
    return ans;
}
int main()
{
    scanf("%d %d %d %d", &n,&m,&s,&t);
    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
", 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;
// pre记录回溯前驱,dis是 cost*w ,pre取代了level数组
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[s] = 1;
    flow[s] = inf;
    dis[s] = 0;
    pre[s] = 0;
    q.push(s);
    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
", i,dis[i]);
    // printf("pret=%d
", pre[t]);
    if (pre[t] == -1)
        return 0;
    return 1;
}
void update()
{
    int now = t;
    while(now!=s)
    {
        node &temp = f[pre[now]][pos[now]];
        temp.w -= flow[t];
        node &temp2=f[now][temp.position];
        temp2.w+= flow[t];
        now = pre[now];
    }
    mincost += flow[t] * dis[t];
    maxflow += flow[t];
}
long long mfmc()
{
    while(spfa())
    {
        update();
    }
}
int main()
{
    scanf("%d %d %d %d", &n,&m,&s,&t);
    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
",maxflow, mincost);
    return 0;    
} 

dinic+spfa 费用流 ((n^2m))

#include<bits/stdc++.h>
using namespace std;
const int N = 1006,const_w=2e7+5;
const long long inf = 1e17;
int T,n, m, qx,zx,a,b,c,d,e,r[N];
int pre[N];
bool use[N];
long long mcost,dis[N],flow[N],maxflow;
// pre记录回溯前驱,dis是 cost*w ,pre取代了level数组
struct node
{
    int link;
    int w;
    int cost;
    int position;
};
vector<node> f[N];
queue<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()//最短路
{
    while(!q.empty())
        q.pop();
    for (int i = 0; i <=2*n+1;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],(long long)xx.w);
                if(use[xx.link]==0)
                {
                    use[xx.link] = 1;
                    q.push(xx.link);
                }
            }
        }
    }
    if (pre[zx] == -1)
        return 0;
    return 1;
}
long long dfs(int x,long long flow)
{
    long long ans = 0;
    if(x==2*n+1||flow==0)
        return flow;
    for(auto &i:f[x])//结构体无法在外部改变,外面只是创建了一个临时变量(相当结构体内部变量是私有属性)
    {
        if(i.w>0&&dis[i.link]==dis[x]+i.cost)// 保证边权不为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()
{
    qx = 0;
    zx = 2*n + 1;
    memset(flow, 0, sizeof(flow));
    mcost = 0;
    maxflow = 0;
    while(spfa())
    {
        maxflow+=dfs(0,inf);
    }
}

高精

搞烦了实在不行换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;
}
原文地址:https://www.cnblogs.com/cherrypill/p/13154656.html