• 差分约束学习笔记


    差分约束可以求出这样的不等式组的 ({x_i}) 的一组整数解

    [x_{a_1}-x_{b_1} le c_1\ x_{a_2}-x_{b_2} le c_2\ vdots\ x_{a_n}-x_{b_n} le c_n\ ]

    对于一个不等式可以移个项 (x_{a_i}le c_i+x_{b_i})

    然后可以想到最短路松弛操作

    用所有连接 (v) 的权值为 (w_i)((u_i,v)),用来更新 (dis_v)

    (dis_{v}=min{dis_{u_i}+w_i})

    因为 (dis_v) 是取了最小的,所以对于所有连接 (v) 的权值为 (w_i)((u_i,v)),都有:

    (dis_{v}le dis_{u_i}+w_i)

    所以对每一个约束 (x_{a_i}le c_i+x_{b_i}),连一条 (b_i)(a_i) 的边,权值为 (c_i)

    这样跑完最短路,({dis_i}) 就能满足原来的不等式组了,而对于 ({dis_i+d}) 也是满足的

    若有负环,则原不等式组无解

    当然,不等式组的 "(le)" 都可以换成 "(ge)",只不过变成跑最长路了(这里要注意,很容易搞错要跑最长路还是最短路,要看你的不等式的情况)

    若题中既有 "(le)" 也有 "(ge)",将 "(le)" 或 "(ge)" 的乘个 (-1) 将其统一一致


    Luogu P1993 小 K 的农场

    下文为表述方便,将 (a)(b) 长度为 (c) 的有向边表示为 (a stackrel{c}{longrightarrow} b)

    题目描述

    小 K 在 MC 里面建立很多很多的农场,总共 (n) 个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得一些含糊的信息(共 (m) 个),以下列三种形式描述:

    • 农场 (a) 比农场 (b) 至少多种植了 (c) 个单位的作物;

    • 农场 (a) 比农场 (b) 至多多种植了 (c) 个单位的作物;

    • 农场 (a) 与农场 (b) 种植的作物数一样多。

    但是,由于小 K 的记忆有些偏差,所以他想要知道存不存在一种情况,使得农场的种植作物数量与他记忆中的所有信息吻合。

    思路
    信息一:(x_age x_b+cimplies x_b le x_a-c),所以连 (a stackrel{-c}{longrightarrow} b)

    信息二:(x_ale x_b+c),所以连 (b stackrel{c}{longrightarrow} a)

    信息三:(x_a=x_bimplies x_a le x_b,x_b le x_a),所以连 (astackrel{0}{longrightarrow} b)(bstackrel{0}{longrightarrow} a)

    判断负环时,记一个 (cnt_i) 表示起点到 (i) 的最短路上有多少个点,若大于 (n) 说明这条路径走了重复的点,也就说明有负环

    #include <algorithm>
    #include <utility>
    #include <vector>
    #include <cstdio>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int maxn = 5e3 + 10;
    const ll inf = 1e15;
    vector<pair<int,int> > edge[maxn];
    int n,m,cnt[maxn];
    bool vis[maxn];
    ll dis[maxn];
    inline bool spfa(int s) {
    	fill(dis,dis+maxn,inf);
    	queue<int> q;
    	vis[s] = true;
    	q.push(s);
    	dis[s] = 0;
    	cnt[s] = 1;
    	for (;q.size();) {
    		int now = q.front(); q.pop();
    		vis[now] = false;
    		for (size_t i = 0;i < edge[now].size();i++) {
    			int to = edge[now][i].first,w = edge[now][i].second;
    			if (dis[to] > dis[now]+w) {
    				dis[to] = dis[now]+w;
    				cnt[to] = cnt[now]+1;
    				if (cnt[to] > n) return false;
    				if (!vis[to]) {
    					vis[to] = true;
    					q.push(to);
    				}
    			}
    		}
    	}
    	return true;
    }
    int main() {
    	scanf("%d%d",&n,&m);
    	for (int i = 1,k,u,v,w;i <= m;i++) {
    		scanf("%d%d%d",&k,&u,&v);
    		if (k == 1) {
    			scanf("%d",&w);
    			edge[u].push_back(make_pair(v,-w));
    		}
    		if (k == 2) {
    			scanf("%d",&w);
    			edge[v].push_back(make_pair(u,w));
    		}
    		if (k == 3) {
    			edge[u].push_back(make_pair(v,0));
    			edge[v].push_back(make_pair(u,0));
    		}
    	}
    	bool ans = true;
    	for (int i = 1;i <= n;i++) if (!cnt[i]) ans &= spfa(i);
    	puts(ans ? "Yes" : "No");
    	return 0;
    }
    

    [SCOI2011]糖果

    题目描述

    幼儿园里有 (n) 个小朋友,( ext{lxhgww}) 老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。同时小朋友们会提出一些如 k a b 的要求

    如果 k=1, 表示第 (a) 个小朋友分到的糖果必须和第 (b) 个小朋友分到的糖果一样多;

    如果 k=2, 表示第 (a) 个小朋友分到的糖果必须少于第 (b) 个小朋友分到的糖果;

    如果 k=3, 表示第 (a) 个小朋友分到的糖果必须不少于第 (b) 个小朋友分到的糖果;

    如果 k=4, 表示第 (a) 个小朋友分到的糖果必须多于第 (b) 个小朋友分到的糖果;

    如果 k=5, 表示第 (a) 个小朋友分到的糖果必须不多于第 (b) 个小朋友分到的糖果;

    思路

    首先 (x_i ge 1,iin [1,n]),令 (x_0 = 0),则 (x_i ge x_0+1)

    这里我们跑最长路方便一些,所有的不等式转成 "(ge)"

    k=1(x_a=x_bimplies x_a ge x_b,x_b ge x_a),连 (astackrel{0}{longrightarrow} b)(bstackrel{0}{longrightarrow} a)

    k=2(x_a < x_b implies x_a le x_b-1 implies x_b ge x_a+1),连 (astackrel{1}{longrightarrow} b)

    k=3(x_a ge x_b),连 (bstackrel{0}{longrightarrow} a)

    k=4(x_a > x_b implies x_a ge x_b+1),连 (bstackrel{1}{longrightarrow} a)

    k=3(x_b ge x_a),连 (astackrel{0}{longrightarrow} b)

    因为都是整数,就把 "(>)" 和 "(<)" 变成了 "(ge)" 和 "(le)"

    #include <algorithm>
    #include <utility>
    #include <vector>
    #include <cstdio>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int maxn = 1e5 + 10;
    vector<pair<int,int> > edge[maxn];
    int n,k,cnt[maxn];
    bool vis[maxn];
    ll dis[maxn];
    inline bool spfa(int s) {
    	queue<int> q;
    	vis[s] = true;
    	q.push(s);
    	dis[s] = 0;
    	cnt[s] = 1;
    	for (;q.size();) {
    		int now = q.front(); q.pop();
    		vis[now] = false;
    		for (size_t i = 0;i < edge[now].size();i++) {
    			int to = edge[now][i].first,w = edge[now][i].second;
    			if (dis[to] < dis[now]+w) {
    				dis[to] = dis[now]+w;
    				cnt[to] = cnt[now]+1;
    				if (cnt[to] > n+1) return false;
    				if (!vis[to]) {
    					vis[to] = true;
    					q.push(to);
    				}
    			}
    		}
    	}
    	return true;
    }
    int main() {
    	for (scanf("%d%d",&n,&k);k--;) {
    		int x,a,b;
    		scanf("%d%d%d",&x,&a,&b);
    		if (x == 1) {
    			edge[a].push_back(make_pair(b,0));
    			edge[b].push_back(make_pair(a,0));
    		}
    		if (x == 2) edge[a].push_back(make_pair(b,1));
    		if (x == 3) edge[b].push_back(make_pair(a,0));
    		if (x == 4) edge[b].push_back(make_pair(a,1));
    		if (x == 5) edge[a].push_back(make_pair(b,0));
    		if ((x == 2 || x == 4) && a == b) return printf("-1")*0;
    	}
    	for (int i = 1;i <= n;i++) edge[0].push_back(make_pair(i,1));
    	bool noans = spfa(0);
    	if (!noans) return printf("-1")*0;
    	long long ans = 0;
    	for (int i = 1;i <= n;i++) ans += dis[i];
    	printf("%lld",ans);
        return 0;
    }
    

    Luogu P4926 [1007]倍杀测量者

    题目描述

    当一位选手 A 的分数不小于选手 B 的分数 (k)(k>0))倍时,我们称选手 A (k)倍杀了选手 B ,选手 B 被选手 A (k)倍杀

    训练前有不少选手立下了诸如“我没 (k) 倍杀选手X,我就女装”,“选手Y把我 (k) 倍杀,我就女装”的 Flag。与此同时,一些选手的成绩是已知的

    Patchouli 为了维持机房秩序,放宽了选手们的 Flag 限制。Patchouli 设定了一个常数 (T),立下“我没 (k) 倍杀选手X就女装”的选手只要成功 ((k-T)) 倍杀了选手 X,就不需要女装。同样的,立下“选手Y把我 (k) 倍杀我就女装”的选手只要没有成功被选手Y (k+T) 倍杀,也不需要女装

    Scarlet 想要确定最大的实数 (T) 使得赛后一定有选手女装

    思路

    首先令 (x_0 = 1),所以已知分数的选手 A,有 (x_A = c_Ax_0implies x_A ge c_Ax_0,x_0ge frac{1}{c_A}x_A),连 (0stackrel{c_A}{longrightarrow} A)(Astackrel{frac{1}{c_A}}{longrightarrow} 0)

    若要没人女装,所有的限制都应满足

    对选手 B 立下 Flag 1 的选手 A 要满足 (x_A > (k-T)x_B),连 (Bstackrel{k-T}{longrightarrow} A)

    对选手 B 立下 Flag 2 的选手 A 要满足 (x_B < (k+T)x_A) 也就是 (x_A > frac{1}{(k+T)}x_B),连 (Bstackrel{frac{1}{(k+T)}}{longrightarrow} A)

    所以当无解时有人女装,二分答案 (T),跑乘积最长路判断。

    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <queue>
    using namespace std;
    const double eps = 1e-8;
    const int maxn = 1000 + 10;
    typedef long long ll;
    struct flags { ll o,a,b,k; } q[maxn<<1];
    vector<pair<int,double> > edge[maxn];
    ll n,s,t,cnt[maxn];
    double dis[maxn];
    bool vis[maxn];
    inline bool check(double mid) {
    	for (ll i = 0;i <= n;i++) {
    		edge[i].clear();
    		vis[i] = cnt[i] = dis[i] = 0;
    	}
    	for (ll i = 1;i <= s+t;i++) {
    		if (q[i].o == 1) edge[q[i].b].push_back(make_pair(q[i].a,1.*q[i].k-mid));
    		if (q[i].o == 2) edge[q[i].b].push_back(make_pair(q[i].a,1./(1.*q[i].k+mid)));
    		if (q[i].o == 3) {
    			edge[q[i].a].push_back(make_pair(0,1./q[i].k));
    			edge[0].push_back(make_pair(q[i].a,1.*q[i].k));
    		}
    	}
    	queue<ll> q;
    	for (;q.size();q.pop());
    	for (q.push(0),dis[0] = 1,vis[0] = true;q.size();) {
    		ll now = q.front(); q.pop();
    		vis[now] = false;
    		for (size_t i = 0;i < edge[now].size();i++) {
    			double w = edge[now][i].second;
    			ll to = edge[now][i].first;
    			if (dis[to] < dis[now]*w) {
    				dis[to] = dis[now]*w;
    				if ((cnt[to] = cnt[now]+1) >= n) return true;
    				if (!vis[to]) { q.push(to); vis[to] = true; }
    			}
    		}
    	}
    	return false;
    }
    signed main() {
    	scanf("%lld%lld%lld",&n,&s,&t);
    	double l = 0,r = 1e10,mid,ans = -124;
    	for (ll i = 1;i <= s;i++) scanf("%lld%lld%lld%lld",&q[i].o,&q[i].a,&q[i].b,&q[i].k);
    	for (ll i = 1;i <= t;i++) scanf("%lld%lld",&q[s+i].a,&q[s+i].k),q[s+i].o = 3;
    	for (;r-l >= eps;check(mid = (l+r)/2.) ? ans = mid,l = mid+eps : r = mid-eps);
    	if (ans < 0) printf("-1");
    	else printf("%.9lf",ans);
    	return 0;
    }
    
  • 相关阅读:
    SSM框架整合常见错误
    4楼B座--内心的梦想,需要我们用心去实现
    打鸡血的废话
    聆听小故事
    php algorithm
    面试笔试常考的mysql 数据库操作group by
    linux mysqli extension is missing
    模仿与创新
    聊天室刷屏的简单原理实现
    程序员迷茫的未来
  • 原文地址:https://www.cnblogs.com/lrj124/p/14410178.html
Copyright © 2020-2023  润新知