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;
    }
    
  • 相关阅读:
    To select the file to upload we can use the standard HTML input control of type
    Cascading Menu Script using Javascript Explained
    网站首页head区代码规范
    轻松掌握 Java 泛型
    JDK 5.0 中的泛型类型学习
    如何在firefox下获取下列框选中option的text
    是同步方法还是 synchronized 代码? 详解多线程同步规则
    javascript select option对象总结
    Select的动态取值(Text,value),添加,删除。兼容IE,FireFox
    javascript在ie和firefox下的一些差异
  • 原文地址:https://www.cnblogs.com/zjlcnblogs/p/11166405.html
Copyright © 2011-2022 走看看