[BZOJ 1797][BZOJ 3258]最小割的唯一性判定

问题一:是否存在一个最小代价路径切断方案,其中该道路被切断? 问题二:是否对任何一个最小代价路径切断方案,都有该道路被切断? 现在请你回答这两个问题。


最小割唯一性判定


jcvb:

在残余网络上跑tarjan求出所有SCC,记id[u]为点u所在SCC的编号。显然有id[s]!=id[t](否则s到t有通路,能继续增广)。

①对于任意一条满流边(u,v),(u,v)能够出现在某个最小割集中,当且仅当id[u]!=id[v];
②对于任意一条满流边(u,v),(u,v)必定出现在最小割集中,当且仅当id[u]==id[s]且id[v]==id[t]。

<==将每个SCC缩成一个点,得到的新图就只含有满流边了。那么新图的任一s-t割都对应原图的某个最小割,从中任取一个把id[u]和id[v]割开的割即可证明。


<==:假设将(u,v)的边权增大,那么残余网络中会出现s->u->v->t的通路,从而能继续增广,于是最大流流量(也就是最小割容量)会增大。这即说明(u,v)是最小割集中必须出现的边。




#include 
#include 
#include 
#include 
#include 
#include 
#define maxn 200000
using namespace std;

const int inf = 0x7fffffff;

struct Edge{
	int to, next, w;
}edge[maxn];

int from[maxn], to[maxn];

int h[maxn], cnt = 1;

void add(int u, int v, int w){
	cnt ++;
	edge[cnt].to = v;
	edge[cnt].next = h[u];
	edge[cnt].w = w;
	h[u] = cnt;
	swap(u, v);w = 0;
	cnt ++;
	edge[cnt].to = v;
	edge[cnt].next = h[u];
	edge[cnt].w = w;
	h[u] = cnt;
}

int n, m, S, T;

queueQ;

int d[4010];

bool BFS(){
	Q.push(S);
	memset(d, -1, sizeof d);
	d[S] = 0;
	while(!Q.empty()){
		int u = Q.front();Q.pop();
		for(int i = h[u]; i; i = edge[i].next){
			if(!edge[i].w)continue;
			int v = edge[i].to;
			if(d[v] == -1){
				d[v] = d[u] + 1;
				Q.push(v);
			}
		}
	}return d[T] != -1;
}

int DFS(int x, int a){
	if(a == 0 || x == T)return a;
	int used = 0, f;
	for(int i = h[x]; i; i = edge[i].next){
		int v = edge[i].to;
		if(d[v] == d[x] + 1){
			f = DFS(v, min(edge[i].w, a - used));
			edge[i].w -= f;
			edge[i ^ 1].w += f;
			used += f;
			if(used == a)return used;
		}
	}
	if(!used)d[x] = -1;
	return used;
}

void Dinic(){
	int ans = 0;
	while(BFS())
		ans += DFS(S, inf);
}

int low[maxn], pre[maxn], dfs_clock, sccno[maxn], scc_cnt;

stacks;

void Tarjan(int u){
	low[u] = pre[u] = ++ dfs_clock;
	s.push(u);
	for(int i = h[u]; i; i = edge[i].next){
		if(!edge[i].w)continue;
		int v = edge[i].to;
		if(!pre[v])Tarjan(v), low[u] = min(low[u], low[v]);
		else if(!sccno[v])low[u] = min(low[u], pre[v]);
	}
	if(low[u] == pre[u]){
		++ scc_cnt;
		for(;;){
			int v = s.top();s.pop();
			sccno[v] = scc_cnt;
			if(v == u)break;
		}
	}
}

int main(){
    freopen("mincut.in" ,"r",stdin );
	freopen("mincut.out","w",stdout);
	scanf("%d%d%d%d", &n, &m, &S, &T);
	int w;
	for(int i = 1; i <= m; i ++){
		scanf("%d%d%d", &from[i], &to[i], &w);
		add(from[i], to[i], w);
	}
	Dinic();
	for(int i = 1; i <= n; i ++)
	    if(!pre[i])Tarjan(i);
	for(int i = 1; i <= m; i ++){
		int u = from[i], v = to[i];
		if(edge[i << 1].w){
			printf("0 0\n");
			continue;
		}
		if(sccno[u] == sccno[v])
		    printf("0 0\n");
		else if(sccno[u] == sccno[S] && sccno[v] == sccno[T])
		    printf("1 1\n");
		else printf("1 0\n");
	}
	return 0;
}

3258: 秘密任务
Description


Alice听说在一片神奇的大陆MagicLand,有一个古老的传说…… 
 很久很久以前,那个时候 MagicStates共和国刚刚成立。 反对新政府的势力虽已被镇压,但仍然在暗地活动。这一次,情报局得到了一个令人震惊的消息,被软禁在首都府邸中的Frank ——著名的反对派领袖,秘密逃出首都,去往反对派的大本营。根据相关的情报,Frank计划通过城市之间 发达的高速公路,经过最短的路程抵达目的地。不妨将 MagicStates共和国简化为由N个城市,M条高速公路构成的连通的无向图,首都为城市1,反对派的大本营为城市N。 
每条高速公路连接两个不同的城市,且路程是已知的。而Frank选择了一条从城市1到城市N的最短路径作为他的逃跑路线。为了阻止Frank,共和国总统决定在某些城市的高速公路的出入口设立检查 点,在Frank经过检查点时将他逮捕。 
举例来说,如果有一条高速公路连接城市u和城市v,在这条公路的城市u或城市v的出入口设立检查点,那么Frank经过高速公路时就会被发现。特别的是,由于城市N实际上处在反对派的控制下,所以不能在城市N设立检查点。


然而在任何城市设立检查点都需要一定的费用。更具体的,若在城市 u设立k个检查点,就要花费 Au乘以k的代价,其中Au是城市u的相关参数。值得注意的是,这个代价与这k个检查点具体设在哪些公路的出入口无关,于是,总统责令情报局拟定一个方案,花费最小的代价使得无论Frank选择哪条最短路线,都会在(除城市N以外)某个城市的高速公路出入口被发现。读到这里,Alice很想知道阻止Frank所需要花费的最小代价,并且她还希 望知道最优方案是否是唯一的。只好再请你帮助她了。 
注意,我们称两个方案不同当且仅当存在某城市k,两种方案中在城市 k的检查点的设置(而不仅是数目)是不同的。 
注意,输入文件包含多组测试数据。


BZOJ3258秘密任务
最小割唯一性判定
这才是真正的最小割唯一性判定
注意一个二元组不能随便建。
要解方程。
此题要拆边,对应两条边两个点的属性
不要任性T^T



#include 
#include 
#include 
#include 
#include 
#include 
#define maxn 10010
using namespace std;

typedef long long ll;

int n, m, S, T, size;
struct Edge{int to, next, w;}edge[100010];

int h[maxn], cnt, val[maxn], fr[100010], to[100010], C;
void add(int u, int v, int w){
	C ++, fr[C] = u, to[C] = v;
	cnt ++;
	edge[cnt].to = v;
	edge[cnt].next = h[u];
	edge[cnt].w = w;
	h[u] = cnt;
	swap(u, v);w = 0;
	cnt ++;
	edge[cnt].to = v;
	edge[cnt].next = h[u];
	edge[cnt].w = w;
	h[u] = cnt;
}
queueQ;
namespace spfa{
	struct Edge_{int to, next, dis;}G[100010];
	int cnt, h[maxn];
	ll dis[maxn], disT[maxn];
	bool vis[maxn];
	void addG(int u, int v, int d){
		cnt ++;
		G[cnt].to = v;
		G[cnt].next = h[u];
		G[cnt].dis = d;
		h[u] = cnt;
	}
	void work(){
		cnt = 0;
		memset(h, 0, sizeof h);
		int u, v, d;
		for(int i = 1; i <= m; i ++){
			scanf("%d%d%d", &u, &v, &d);
			addG(u, v, d);
			addG(v, u, d);
		}
		memset(dis, 0x7f, sizeof dis);
		memset(vis, 0, sizeof vis);
		dis[S] = 0;Q.push(S);
		while(!Q.empty()){
			int u = Q.front();Q.pop();
			for(int i = h[u]; i; i = G[i].next){
				int v = G[i].to;
				if(dis[v] > dis[u] + G[i].dis){
					dis[v] = dis[u] + G[i].dis;
					if(!vis[v])vis[v] = true, Q.push(v);
				}
			}
			vis[u] = false;
		}
		memset(disT, 0x7f, sizeof disT);
		memset(vis, 0, sizeof vis);
		disT[T] = 0;Q.push(T);
		while(!Q.empty()){
			int u = Q.front();Q.pop();
			for(int i = h[u]; i; i = G[i].next){
				int v = G[i].to;
				if(disT[v] > disT[u] + G[i].dis){
					disT[v] = disT[u] + G[i].dis;
					if(!vis[v])vis[v] = true, Q.push(v);
				}
			}
			vis[u] = false;
		}
		size = n;
		for(int i = 1; i < n; i ++)
			for(int j = h[i]; j; j = G[j].next){
				int v = G[j].to;
				if(dis[T] == dis[i] + G[j].dis + disT[v]){
					++ size;
					add(i, size, val[i]);
					add(size, v, val[v]);
					//add(i, v, min(val[i], val[v]));
				}
		}
	}
}

const int inf = 0x7fffffff;

int d[maxn];
bool BFS(){
	memset(d, -1, sizeof d);
	d[S] = 0;
	Q.push(S);
	while(!Q.empty()){
		int u = Q.front();Q.pop();
		for(int i = h[u]; i; i = edge[i].next){
			if(!edge[i].w)continue;
			int v = edge[i].to;
			if(d[v] == -1){
				d[v] = d[u] + 1;
				Q.push(v);
			}
		}
	}return d[T] != -1;
}

ll DFS(int x, ll a){
	if(a == 0 || x == T)return a;
	ll used = 0, f;
	for(int i = h[x]; i; i = edge[i].next){
		int v = edge[i].to;
		if(d[v] == d[x] + 1){
			f = DFS(v, min(a - used, (ll)edge[i].w));
			edge[i].w -= f;
			edge[i ^ 1].w += f;
			used += f;
			if(used == a)return used;
		}
	}
	if(!used)d[x] = -1;
	return used;
}

ll Dinic(){
	ll ans = 0;
	while(BFS())
		ans += DFS(S, inf);
	return ans;
}

int sccno[maxn], pre[maxn], low[maxn], dfs_clock, scc_cnt;
stacks;
void Tarjan(int u){
	pre[u] = low[u] = ++ dfs_clock;
	s.push(u);
	for(int i = h[u]; i; i = edge[i].next){
		if(!edge[i].w)continue;
		int v = edge[i].to;
		if(!pre[v])Tarjan(v), low[u] = min(low[u], low[v]);
		else if(!sccno[v])low[u] = min(low[u], pre[v]);
	}
	if(low[u] == pre[u]){
		++ scc_cnt;
		for(;;){
			int v = s.top();s.pop();
			sccno[v] = scc_cnt;
			if(v == u)break;
		}
	}
}

int main(){
	//freopen("secret.in", "r", stdin);
	//freopen("secret.out", "w", stdout);
	int test;
	scanf("%d", &test);
	while(test --){
		C = 0;
		scanf("%d%d", &n, &m);
		for(int i = 1; i < n; i ++)
			scanf("%d", &val[i]);
		val[n] = 0x7fffffff;
		memset(h, 0, sizeof h);
  		cnt = 1;
		S = 1, T = n;
		spfa::work();
		ll ans = Dinic();
		
		memset(pre, 0, sizeof pre);
		memset(low, 0, sizeof low);
		memset(sccno, 0, sizeof sccno);
		scc_cnt = dfs_clock = 0;

		for(int i = 1; i <= size; i ++)
		    if(!pre[i])Tarjan(i);
	    for(int i = 1; i <= C; i ++){
			int u = fr[i], v = to[i];
			if(edge[i << 1].w || sccno[u] == sccno[v])continue;
			if(sccno[u] == sccno[S] && sccno[v] == sccno[T])
			    continue;
			printf("No %lld\n", ans);
			goto Mark;
	   }
	   printf("Yes %lld\n", ans);
	   Mark:;
	}

	return 0;
}


重点是看S和T是否联通

我们的思想是Tarjan找桥边。

如果存在可能不是最小割上的边

那么就不唯一。

你可能感兴趣的:(图论--网络流)