zoukankan      html  css  js  c++  java
  • 【JZOJ3348】【NOI2013模拟】秘密任务 (Standard IO) (最小割唯一性的判定)

    题目大意

    给出一个无向图,一个人会从1号点沿着最短路走到n号点(可能有多条路径),而你需要在某些边的一端设置障碍,使他最终不能够到达n号点,求最小代价,并判断方案是否唯一。

    分析

    首先建出最短路图。我们可以把原来的边((u,v))拆成两段:((u,x))((x,v));割掉((u,x))的代价为(a[u]),割掉((x,v))的代价为(a[v])。我们的目的是用最小代价割掉最短路图上某些边,使得(1)走不到(n),这是个最小割模型,于是做一遍最大流就求出了第二问。

    问题难在第一问,如何判断最小割是否唯一?

    一个不是很显然的结论是:设残量网络中源点(S)通过未满流边能够到达的点集为(S_0),通过未满流边能够到达汇点(T)的点集为(T_0),若一条边((u,v))满足(uin S_0,vin T_0),这条边必定存在于任意一个最小割中。

    反证法证明:假设((u,v))不能割,那可以将它的容量视为无穷大,根据题设,我们又能增加(S)(T)的流量,这与已经求得最大流矛盾,故((u,v))是必须要割的边。

    有了这个结论,我们直接将必割边的代价加起来,如果等于所求最小割,那么方案唯一,否则方案不唯一。

    Code

    #include <cstdio>
    #include <cstring>
    
    typedef long long ll;
    const int N = 100007, M = 100007;
    const ll INF = (1ll << 60);
    ll min(ll a, ll b) { return a < b ? a : b; }
    
    int test;
    int n, m, S, T, cnt, s1[N], s2[N];
    ll ans, sum, dis[N], a[N], len[M];
    int tot, st[N], to[M], nx[M];
    int total, head[N], go[M], next[M];
    int h, t, q[N], vis[N];
    void clear1() { tot = 0; memset(st, 0, sizeof(st)); memset(to, 0, sizeof(to)); memset(nx, 0, sizeof(nx)); }
    void clear2() { total = 0; memset(head, 0, sizeof(head)); memset(go, 0, sizeof(go)); memset(next, 0, sizeof(next)); }
    void add(int u, int v, ll w) { to[++tot] = v, nx[tot] = st[u], len[tot] = w, st[u] = tot; }
    void link(int u, int v) { go[++total] = v, next[total] = head[u], head[u] = total; }
    
    void spfa()
    {
    	memset(dis, 0x3f, sizeof(dis));
    	memset(vis, 0, sizeof(vis));
    	h = 1, q[t = 1] = 1, vis[1] = 1, dis[1] = 0;
    	while (h <= t)
    	{
    		int u = q[h++];
    		vis[u] = 0;
    		for (int i = st[u]; i; i = nx[i])
    			if (dis[u] + len[i] < dis[to[i]])
    			{
    				dis[to[i]] = dis[u] + len[i];
    				if (!vis[to[i]]) q[++t] = to[i], vis[to[i]] = 1;
    			}
    	}
    }
    void dfs(int u)
    {
    	vis[u] = 1;
    	for (int i = st[u]; i; i = nx[i])
    		if (dis[to[i]] + len[i] == dis[u])
    		{
    			link(u, to[i]);
    			if (!vis[to[i]]) dfs(to[i]);
    		}
    }
    int bfs()
    {
    	memset(dis, 0, sizeof(dis));
    	h = 1, q[t = 1] = S, dis[S] = 1;
    	while (h <= t)
    	{
    		int u = q[h++];
    		for (int i = st[u]; i; i = nx[i])
    			if (!dis[to[i]] && len[i] > 0)
    			{
    				dis[to[i]] = dis[u] + 1, q[++t] = to[i];
    				if (to[i] == T) return 1;
    			}
    	}
    	return 0;
    }
    ll dinic(int u, ll flow)
    {
    	if (u == T) return flow;
    	ll rest = flow, tmp;
    	for (int i = st[u]; i; i = nx[i])
    		if (dis[to[i]] == dis[u] + 1 && len[i] > 0)
    		{
    			tmp = dinic(to[i], min(rest, len[i]));
    			if (!tmp) dis[to[i]] = 0;
    			len[i] -= tmp, len[i ^ 1] += tmp, rest -= tmp;
    			if (!rest) return flow;
    		}
    	return flow - rest;
    }
    void getset()
    {
    	memset(s1, 0, sizeof(s1)); memset(s2, 0, sizeof(s2));
    	memset(vis, 0, sizeof(vis));
    	h = 1, q[t = 1] = S, vis[S] = 1;
    	while (h <= t)
    	{
    		int u = q[h++]; s1[u] = 1;
    		for (int i = st[u]; i; i = nx[i]) if (len[i] > 0 && !vis[to[i]]) q[++t] = to[i], vis[to[i]] = 1;
    	}
    	memset(vis, 0, sizeof(vis));
    	h = 1, q[t = 1] = T, vis[T] = 1;
    	while (h <= t)
    	{
    		int u = q[h++]; s2[u] = 1;
    		for (int i = st[u]; i; i = nx[i]) if (len[i ^ 1] > 0 && !vis[to[i]]) q[++t] = to[i], vis[to[i]] = 1;
    	}
    }
    
    int main()
    {
    	scanf("%d", &test);
    	while (test--)
    	{
    		clear1(), clear2();
    		scanf("%d%d", &n, &m);
    		for (int i = 1; i <= n - 1; i++) scanf("%lld", &a[i]); a[n] = INF;
    		for (int i = 1, u, v, w; i <= m; i++) scanf("%d%d%d", &u, &v, &w), add(u, v, w), add(v, u, w);
    		spfa();
    		memset(vis, 0, sizeof(vis)), dfs(n);
    		clear1(); tot = 1, cnt = n;
    		for (int i = 1; i <= n; i++) for (int j = head[i]; j; j = next[j]) add(go[j], ++cnt, a[go[j]]), add(cnt, go[j], 0), add(cnt, i, a[i]), add(i, cnt, 0);
    		S = 1, T = n, ans = sum = 0;
    		while (bfs()) ans += dinic(S, INF);
    		getset();
    		cnt = n;
    		for (int i = 1; i <= n; i++)
    			for (int j = head[i]; j; j = next[j])
    			{
    				++cnt;
    				if (s1[go[j]] && s2[cnt]) sum += a[go[j]];
    				if (s1[cnt] && s2[i]) sum += a[i];
    			}
    		printf(sum == ans ? "Yes %lld
    " : "No %lld
    ", ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    java 生成二维码
    Web Service测试工具小汇
    ADF控件ID变化引发JS无法定位控件的解决方法
    create database link
    Jdeveloper下Svn的使用
    TortoiseSVN菜单项功能说明
    JSFF或JSF页面加载时触发JavaScript之方法
    更改ORACLE归档路径及归档模式
    更改oracle归档模式路径
    将Oracle数据库设置为归档模式及非归档模式
  • 原文地址:https://www.cnblogs.com/zjlcnblogs/p/11166405.html
Copyright © 2011-2022 走看看