图论板子

好多板子都没打啊...

快读

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

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;
}
posted @ 2020-06-18 23:30  Sakura_Momoko  阅读(208)  评论(0编辑  收藏  举报