• 51Nod 1340 地铁环线


    题目分析

    很恶心的很好的一道差分约束题.

    由于图中有环, 考虑断环为链.
    (Dis_{i->j}) 表示从 (i) 站点到 (j) 站点的环线长度.
    (D_i) 表示从 (1) 站点到 (i) 站点的环线长度.
    (Len) 表示地铁环线总长.
    于是可以整理出下面的式子: (均假定 (i < j))

    1. (Dis_{i->j} + Dis_{j->i} = Len)
    2. (Dis_{i->j} = D_j - D_i)
    3. (Dis_{i->j} geq k => D_j - D_i geq k => D_i leq D_j - k)
    4. (Dis_{i->j} leq k => D_j - D_i leq k => D_j leq D_i + k)
    5. (Dis_{j->i} geq k => Len - Dis_{i->j} geq k => D_j <= D_i - k + Len)
    6. (Dis_{j->i} leq k => Len - Dis_{i->j} leq k => D_i <= D_j + k - Len)

    你会发现差分约束系统中有一个未知量 (Len). 回想差分约束, 怎么判有解来着? 找负环.
    题目又要输出方案数, 那该怎么做呢?
    考虑图上每一个环, 其权值一定可以表示为 (val = x + k imes Len) 的形式. 那么现在分类讨论一下每一个环.

    1. (k = 0)(x < 0) 时, (val < 0), 一定无解.
    2. (k < 0)(x < 0) 时, (val < 0), 那么也是一定无解的.
    3. (k < 0)(x > 0) 时, 可以通过减小 (Len) 来保证 (val > 0).
    4. (k > 0) 时, 可以通过增大 (Len) 来使得 (val > 0).

    也就是说, 如果我们暴力枚举 (Len) 的大小然后找负环时, 我们可以通过找到的负环的 (k) 来确定 (Len) 是需要增大还是减小, 抑或是无解.
    实际上, 这样思考会发现 (Len) 的合法大小一定是一段连续区间. 于是, 我们可以两次二分 (Len) 的大小, 通过找到的负环的 (k) 来缩小答案区间, 并且分别找到 (Len) 区间的左右端点.
    那...怎么判无穷个解呢? 这个简单, 当答案区间大于一定阀值就可以看作是无穷解的.

    代码

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <iostream>
    
    const int N = 50;
    const int M = 1e4;
    const long long INF = 2000000000ll * 50; //Const
    
    int n, m1, m2, Test; //Main
    
    int Head[N + 5], Cnt;
    struct Edge {int Nxt, From, To, Val, k;} E[N + M + 5]; //Edge
    
    int Cnte[N + 5], Pre[M + 5], St;
    long long Dis[N + 5];
    bool Vis[N + 5], Vs[N + 5]; //SPFA
    
    void Fizz();
    void Clear();
    int SPFA(long long);
    void Add(int, int, int, int);
    
    int main() {
    	scanf("%d", &Test);
    	while(Test--) {
    		scanf("%d%d%d", &n, &m1, &m2), Fizz(), St = n + 1;
    		Add(1, n, (n > 1? -1 : 0), 1);
    		for(int i = 1; i < n; i ++) Add(i + 1, i, -1, 0);
    		for(int i = 1; i <= n; i ++) Add(St, i, 0, 0);
    		for(int i = 1; i <= m1; i ++) {
    			int u, v, w;
    			scanf("%d%d%d", &u, &v, &w), ++u, ++v;
    			if(u < v) Add(v, u, -w, 0); else Add(v, u, -w, 1);
    	
    		} for(int i = 1; i <= m2; i ++) {
    			int u, v, w;
    			scanf("%d%d%d", &u, &v, &w), ++u, ++v;
    			if(u < v) Add(u, v, w, 0); else Add(u, v, w, -1);
    		} bool Flag = true; 
    		long long Lans = INF, Rans = 0, L, R;
    		L = 0, R = INF; 
    		while(L <= R) {
    			long long Mid = (L + R) / 2;
    			long long tap = SPFA(Mid);
    			if(tap == 2) Lans = Mid, R = Mid - 1;
    			else if(tap == -1) {
    				Flag = false; break;
    			} else if(tap == 0) L = Mid + 1;
    			else R = Mid - 1;
    		
    		} L = 1, R = 2000000000ll * 50;
    		while(L <= R) {
    			long long Mid = (L + R) / 2;
    			long long tap = SPFA(Mid);
    			if(tap == 2) Rans = Mid, L = Mid + 1;
    			else if(tap == -1) {
    				Flag = false; break;
    			} else if(tap == 0) L = Mid + 1;
    			else R = Mid - 1;
    		
    		} if(!Flag) printf("0
    ");
    		else if(Rans >= 2000000000ll*50) printf("-1
    ");
    		else printf("%lld
    ", std::max(0ll, Rans - Lans + 1));
    	} return 0;
    
    } void Add(int u, int v, int w, int k) {
    	E[++Cnt] = (Edge){Head[u], u, v, w, k}, Head[u] = Cnt;
    
    } void Fizz() {
    	memset(Head, 0, sizeof(Head)), Cnt = 0;
    
    } void Clear() {
    	memset(Dis, 0x3f, sizeof(Dis));
    	memset(Vis, false, sizeof(Vis));
    	memset(Pre, 0, sizeof(Pre));
    	memset(Cnte, 0, sizeof(Cnte));
    
    } int SPFA(long long K) {
    	Clear();
    	std::queue<int> Q;
    	Q.push(St), Dis[St] = 0, Vis[St] = true;
    
    	while(Q.size()) {
    		int tap = Q.front(); Q.pop(), Vis[tap] = false;
    		for(int i = Head[tap]; i; i = E[i].Nxt) {
    			int v = E[i].To;
    			long long DIS = Dis[tap] + E[i].Val + E[i].k * K;
    			if(Dis[v] > DIS) {
    				Pre[v] = i, Cnte[v] = Cnte[tap] + 1, Dis[v] = DIS;
    				
    				if(Cnte[v] > n + 1) {
    					memset(Vs, false, sizeof(Vs));
    					int Sum = 0, tmp = tap;
    					for(int j = tap; j; j = E[Pre[j]].From)
    						if(Vs[j]) {
    							tmp = j; break;
    						} else Vs[j] = true;
    					Sum = E[Pre[tmp]].k;
    					for(int j = E[Pre[tmp]].From; j != tmp; j = E[Pre[j]].From) Sum += E[Pre[j]].k;
    					if(Sum == 0) return -1;
    					if(Sum > 0) return 0;
    					if(Sum < 0) return 1;
    				
    				} if(!Vis[v]) Vis[v] = true, Q.push(v);
    			}
    		}
    	} return 2;
    }
    
  • 相关阅读:
    监控显示器的状态
    使用SymPy
    Java 连接本地数据库失败 Communications link failure
    RabbitMQ的下载和安装
    网络的工作机制
    RabbitMQ
    vue项目:通过高德API根据地址获取经纬度
    oracle数据库报错:ORA00001: 违反唯一约束条件
    Springboot2.6.5整合RabbitMQ
    FFmpeg下载及安装
  • 原文地址:https://www.cnblogs.com/Rothen/p/13871919.html
Copyright © 2020-2023  润新知