zoukankan      html  css  js  c++  java
  • 【SHOI2010】最小生成树(最小割,最小生成树)

    初看题目,发现题目的操作比较复杂。仔细想了一想,发现题目中的操作“把图中除了这条边以外的边,每一条的权值都减少 1 1 1”就等价于“把这条边的权值加 1 1 1”。所以题目的操作就被我们化繁为简了。

    然后继续想:如何才能使 L a b Lab Lab 边一定在最小生成树中?

    画个图看一下(就以样例为例):

    假设现在 L a b Lab Lab 边是 ( 1 , 2 ) (1,2) (1,2),长度为 2 2 2

    那么既然我要保证 L a b Lab Lab 边是最小生成树的一条边,点 1 1 1 2 2 2 就不能在一个环里面,也就是说,点 1 1 1 2 2 2 之间不能存在另一条可选的路径。(注意“边”与“路径”的区别)

    也就是说,在原来的无向图上, 1 → 2 1 ightarrow2 12 的其他路径中,每条路径至少要存在一条边的边权大于 2 2 2,否则在建最小生成树时就有可能被选到。

    比如 1 → 2 1 ightarrow2 12 的其他路径有 1 → 3 → 2 1 ightarrow3 ightarrow2 132 1 → 4 → 2 1 ightarrow4 ightarrow2 142 ……,其中路径 1 → 3 → 2 1 ightarrow3 ightarrow2 132 是不满足条件的,因为这条路径上没有一条边的边权大于 2 2 2,那么我们就有可能选边:

    而不是这样:

    总而言之,设 L a b Lab Lab 边是 ( A , B ) (A,B) (A,B),长度为 l e n len len,那么我们要保证在操作之后的无向图上,不存在一条路径 A → B A ightarrow B AB 使得这条路径上所有边的边权都小于等于 l e n len len

    如何维护?

    一种显然的暴力就是:枚举 A → B A ightarrow B AB 的每一条路径,找到路径上边权的最大值 m a x n maxn maxn,如果 m a x n > l e n maxn>len maxn>len,就不管它,否则就用 l e n − m a x n + 1 len-maxn+1 lenmaxn+1 次操作使得这条边权最大的边的权值加上 l e n − m a x n + 1 len-maxn+1 lenmaxn+1,超过 l e n len len。这时我们就能用最小的代价保证这条路径不可能被选到。

    想到这里很容易想到最小割:找到边权 l e n 0 len_0 len0 小于等于 l e n len len 的每条边 ( u , v ) (u,v) (u,v),并在另一个图上建边 ( u , v , l e n − l e n 0 + 1 ) (u,v,len-len_0+1) (u,v,lenlen0+1),然后再从 A A A B B B 跑最小割,也就是说找到 A → B A ightarrow B AB 每条路径上操作代价最小的一条边。最后跑出来的最小割就是答案。

    代码如下:

    #include<bits/stdc++.h>
    
    #define N 510
    #define M 810
    #define INF 0x7fffffff
    
    using namespace std;
    
    struct edge
    {
    	int u,v,w;
    }e[M];
    
    int n,m,lab,s,t;
    int cnt=1,head[N],cur[N],to[M<<2],nxt[M<<2],c[M<<2];
    int num[N];
    
    queue<int>q;
    
    void adde(int u,int v,int ci)
    {
    	to[++cnt]=v;
    	c[cnt]=ci;
    	nxt[cnt]=head[u];
    	head[u]=cnt;
    	
    	to[++cnt]=u;
    	c[cnt]=0;
    	nxt[cnt]=head[v];
    	head[v]=cnt;
    }
    
    bool bfs()
    {
    	memcpy(cur,head,sizeof(cur));
    	memset(num,-1,sizeof(num));
    	q.push(s);
    	num[s]=0;
    	while(!q.empty())
    	{
    		int u=q.front();
    		q.pop();
    		for(int i=head[u];i;i=nxt[i])
    		{
    			int v=to[i];
    			if(c[i]&&num[v]==-1)
    			{
    				num[v]=num[u]+1;
    				q.push(v);
    			}
    		}
    	}
    	return num[t]!=-1;
    }
    
    int dfs(int u,int minflow)
    {
    	if(!minflow||u==t) return minflow;
    	int preflow=0,nowflow;
    	for(int i=cur[u];i;i=nxt[i])
    	{
    		cur[u]=i;
    		int v=to[i];
    		if(num[v]==num[u]+1&&(nowflow=dfs(v,min(minflow-preflow,c[i]))))
    		{
    			preflow+=nowflow;
    			c[i]-=nowflow;
    			c[i^1]+=nowflow;
    			if(!(minflow-preflow)) break;
    		}
    	}
    	return preflow;
    }
    
    int dinic()
    {
    	int maxflow=0;
    	while(bfs())
    		maxflow+=dfs(s,INF);
    	return maxflow;
    }
    
    int main()
    {
    	scanf("%d%d%d",&n,&m,&lab);
    	for(int i=1;i<=m;i++)
    		scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
    	s=e[lab].u,t=e[lab].v;
    	for(int i=1;i<=m;i++)
    	{
    		if(i!=lab&&e[i].w<=e[lab].w)
    		{
    			adde(e[i].u,e[i].v,e[lab].w-e[i].w+1);//别问我为什么要建双向边,问就是这是dinic,分层图不怕死循环
    			adde(e[i].v,e[i].u,e[lab].w-e[i].w+1);
    		}
    	}
    	printf("%d
    ",dinic());
    	return 0;
    }
    
  • 相关阅读:
    批量启动application pool
    sql server文件另存为的时候,选择文件编码和换行
    insert into 和 where not exists
    tcp slowstart (TCP 慢启动)
    如何在CentOS7上改变网络接口名
    Window系统命令行调用控制面板程序
    Using Let’s Encrypt for free SSL Certs with Netscaler
    端口相关知识学习笔记
    win7下KiWi Syslog服务器的安装与配置
    MPS添加管理设备实例NS的过程
  • 原文地址:https://www.cnblogs.com/ez-lcw/p/14448665.html
Copyright © 2011-2022 走看看