差分约束专题

关于差分约束的相关定义,这篇博客推荐看一下 何为差分约束
接下来通过些题目来实战一下
layout-POJ 3169
House Man-HDU 3440
Intervals-POJ 1201
Kings-POJ 1364
THE MATRIX PROBLEM-HDU 3666
Is the Information Reliable?-POJ 2983
Cashier Employment-HDU 1529
Spy’s Work-HDU 4274


layout

layout poj-3169
这题有两个不等式要求:
对于ML个关系,有 S B − S A ≤ w S_B-S_A\le w SBSAw
对于MD个关系,有 S B − S A ≥ w S_B-S_A\ge w SBSAw
其中 S i S_i Si 表示 i i i 到原点的距离。由于题目还有另外两种情况,

  • 无解则输出-1,无解便是跑spfa出现负圈的情况
  • 无穷大则输出-2,便是通过以上关系并不能更新到第n个点,则说明第n点不受上述条件的限制。

这题算是差分约束的入门题,只有两种基本的关系,在写代码的时候需要进行下转换,使所有式子的不等于号方向统一:

  • 仅有 S B − S A ≤ w S_B-S_A\le w SBSAw S A − S B ≤ − w S_A-S_B\le -w SASBw ,这个时候相当于求最短路
  • 仅有 S A − S B ≥ − w S_A-S_B\ge -w SASBw S B − S A ≥ w S_B-S_A\ge w SBSAw ,这个时候相当于求最长路

代码如下,选取了求最短路的情况:

#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <set>
#include <cstdlib>
#include <iostream>
#include <map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uii;
typedef pair<int, ll> pii;
template<typename T>
inline void rd(T& x)
{
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-5;
int sgn(double x) {
	if (fabs(x) < eps) return 0;
	if (x > 0) return 1;
	return -1;
}
int n, m, st, ed;
int head[N], cntE;
struct edge {
	int to, next;
	int w;
}e[M];
void add(int u, int v, int w) {
	e[cntE].to = v;
	e[cntE].next = head[u];
	e[cntE].w = w;
	head[u] = cntE++;
}
bool inq[N];
int dis[N], neg[N];
int spfa(int st) {
	memset(inq, false, sizeof(bool) * (n + 10));
	memset(dis, inf, sizeof(int) * (n + 10));
	memset(neg, 0, sizeof(int) * (n + 10));
	queue<int>que;
	que.push(st);
	dis[st] = 0;
	inq[st] = true;
	while (!que.empty()) {
		int u = que.front(); que.pop();
		inq[u] = false;
		for (int i = head[u]; ~i; i = e[i].next) {
			int v = e[i].to;
			if (dis[v] > dis[u] + e[i].w) {
				dis[v] = dis[u] + e[i].w;
				if (!inq[v]) {
					que.push(v);
					inq[v] = true;
					++neg[v];
					if (neg[v] >= n) {
						return -1;
					}
				}
			}
		}
	}
	if (dis[n] == inf) return -2;
	return dis[n];
}
int main() {
#ifdef _DEBUG
	FILE* _INPUT = freopen("input.txt", "r", stdin);
	//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif // !_DEBUG
	int cas = 0;
	while (~scanf("%d", &n)) {
		memset(head, -1, sizeof(int) * (n + 10)); cntE = 0;
		rd(m); int m1; rd(m1);
		while (m--) {
			int x, y, w;
			rd(x), rd(y), rd(w);
			add(x, y, w);
		}
		while (m1--) {
			int x, y, w;
			rd(x), rd(y), rd(w);
			add(y, x, -w);
		}
		printf("%d\n", spfa(1));
	}
}

附带一题 HDU-3592
思路跟本题一模一样,可以更深入去理解。


House Man

HDU-3440
这道题的不等式有两种:

  • S i + 1 − S i ≥ 1 S_{i+1}-S_i\ge1 Si+1Si1
  • S s o r t ( i + 1 ) − S s o r t ( i ) ≤ D S_{sort(i+1)}-S_{sort(i)}\le D Ssort(i+1)Ssort(i)D

其中第二项是根据高度排完序之后的顺序,满足每次跳跃的距离 ≤ D \le D D
将不等式符号化成同一方向后直接建图即可。
代码如下:

#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <set>
#include <cstdlib>
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uii;
typedef pair<int, ll> pii;
template<typename T>
inline void rd(T& x)
{
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-5;
int sgn(double x) {
	if (fabs(x) < eps) return 0;
	if (x > 0) return 1;
	return -1;
}
int n, m, st, ed;
int head[N], cntE;
struct edge {
	int to, next;
	int w;
}e[M];
void add(int u, int v, int w) {
	e[cntE].to = v;
	e[cntE].next = head[u];
	e[cntE].w = w;
	head[u] = cntE++;
}
bool inq[N];
int dis[N], neg[N];
int h[N], id[N];
int spfa(int st,int ed) {
	memset(inq, false, sizeof(bool) * (n + 10));
	memset(dis, inf, sizeof(int) * (n + 10));
	memset(neg, 0, sizeof(int) * (n + 10));
	queue<int>que;
	que.push(st);
	dis[st] = 0;
	inq[st] = true;
	while (!que.empty()) {
		int u = que.front(); que.pop();
		inq[u] = false;
		for (int i = head[u]; ~i; i = e[i].next) {
			int v = e[i].to;
			if (dis[v] > dis[u] + e[i].w) {
				dis[v] = dis[u] + e[i].w;
				if (!inq[v]) {
					que.push(v);
					inq[v] = true;
					++neg[v];
					if (neg[v] >= n) {
						return -1;
					}
				}
			}
		}
	}
	if (dis[ed] == inf) return -2;
	return dis[ed];
}

int main() {
#ifdef _DEBUG
	FILE* _INPUT = freopen("input.txt", "r", stdin);
	//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif // !_DEBUG
	int cas = 0, T;
	rd(T);
	while(T--){
//	while (~scanf("%d", &n)) {
		rd(n); int k; rd(k);
		memset(head, -1, sizeof(int) * (n + 10)); cntE = 0;
		for (int i = 1; i <= n; ++i) rd(h[i]);
		for (int i = 1; i < n; ++i) add(i + 1, i, -1);
		for (int i = 1; i <= n; ++i) id[i] = i;
		sort(id + 1, id + 1 + n, [](const int& a, const int& b) {
			return h[a] < h[b];
			});
 		for (int i = 1; i < n; ++i) {
			add(min(id[i], id[i + 1]), max(id[i], id[i + 1]), k);
		}
		printf("Case %d: %d\n", ++cas, spfa(min(id[1], id[n]), max(id[1], id[n])));
	}
	return 0;
}

Intervals

POJ-1201
这题需要将区间表示成前缀和的差值
如: [ L , R ] [L,R] [L,R] 中需要至少 x x x 个数,表示成 S R − S L − 1 ≥ x S_R-S_{L-1}\ge x SRSL1x
所以本题的不等式关系有:

  • S R − S L − 1 ≥ x S_R-S_{L-1}\ge x SRSL1x 这个是题目输入的关系。
  • 另外还有隐藏关系,第 i i i 数可选可不选,因此有 0 ≤ S i + 1 − S i ≤ 1 0\le S_{i+1}-S_{i}\le1 0Si+1Si1

将不等式符号转换成同一方向建边跑 s p f a spfa spfa 即可

#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <set>
#include <cstdlib>
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uii;
typedef pair<int, ll> pii;
template<typename T>
inline void rd(T& x)
{
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-5;
int sgn(double x) {
	if (fabs(x) < eps) return 0;
	if (x > 0) return 1;
	return -1;
}
int n, m, st, ed;
int head[N], cntE;
struct edge {
	int to, next;
	int w;
}e[M];
void add(int u, int v, int w) {
	e[cntE].to = v;
	e[cntE].next = head[u];
	e[cntE].w = w;
	head[u] = cntE++;
}
bool inq[N];
int dis[N], neg[N];
int h[N], id[N];
int spfa(int st,int ed) {
	memset(inq, false, sizeof(bool) * (n + 10));
	memset(dis, -inf, sizeof(int) * (n + 10));
	memset(neg, 0, sizeof(int) * (n + 10));
	queue<int>que;
	que.push(st);
	dis[st] = 0;
	inq[st] = true;
	while (!que.empty()) {
		int u = que.front(); que.pop();
		inq[u] = false;
		for (int i = head[u]; ~i; i = e[i].next) {
			int v = e[i].to;
			if (dis[v] < dis[u] + e[i].w) {
				dis[v] = dis[u] + e[i].w;
				if (!inq[v]) {
					que.push(v);
					inq[v] = true;
					++neg[v];
					if (neg[v] >= n) {
						return -1;
					}
				}
			}
		}
	}
	if (dis[ed] == inf) return -2;
	return dis[ed];
}

int main() {
#ifdef _DEBUG
	FILE* _INPUT = freopen("input.txt", "r", stdin);
	//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif // !_DEBUG
	int cas = 0, T;
//	rd(T);
//	while(T--){
	ed = 6e4;
	while (~scanf("%d", &n)) {
		memset(head, -1, sizeof(int) * (200000 + 10)); cntE = 0;
		int maxn = 0;
		for (int i = 1; i <= n; ++i) {
			int l, r, w; rd(l), rd(r), rd(w);
			++r; //避免l-1后变成-1,便选择将r+1
			add(l, r, w);
			maxn = max(maxn, r);
		}
		for (int i = 1; i <= maxn; ++i) {
			add(i - 1, i, 0);
			add(i, i - 1, -1);
		}
		printf("%d\n", spfa(0,maxn));
	}
	return 0;
}

King

POJ-1364
这题题面有点难懂,下标的表示没写好,大致意思是:
有一个 a n a_n an的序列,其中满足一些前缀和条件: a S i + a S i + 1 + a S i + 2 + . . . + a S i + n i a_{S_i}+a_{S_{i}+1}+a_{S_{i}+2}+...+a_{S_{i}+n_i} aSi+aSi+1+aSi+2+...+aSi+ni < \lt < 或者 > \gt > k i ki ki
所以这题有两种不等式:

  • F ( i ) F(i) F(i) 为序列 a 1 到 a i a_1到a_i a1ai 的前缀和。有 F ( s i + n i ) − F ( s i − 1 ) < k i F(s_i+n_i)-F(s_i-1)\lt k_i F(si+ni)F(si1)<ki
    或者 F ( s i + n i ) − F ( s i − 1 ) > k i F(s_i+n_i)-F(s_i-1)\gt k_i F(si+ni)F(si1)>ki
  • 最后由于每一个 a i a_i ai 的范围是未知的,所以只能确立一个超级源点来连接所有的数,最终判断结果是否有负圈就行了
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <set>
#include <cstdlib>
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uii;
typedef pair<int, ll> pii;
template<typename T>
inline void rd(T& x)
{
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-5;
int sgn(double x) {
	if (fabs(x) < eps) return 0;
	if (x > 0) return 1;
	return -1;
}
int n, m, st, ed;
int head[N], cntE;
struct edge {
	int to, next;
	int w;
}e[M];
void add(int u, int v, int w) {
	e[cntE].to = v;
	e[cntE].next = head[u];
	e[cntE].w = w;
	head[u] = cntE++;
}
bool inq[N];
int dis[N], neg[N];
int h[N], id[N];
bool spfa(int st) {
	memset(inq, false, sizeof(bool) * (n + 10));
	memset(dis, inf, sizeof(int) * (n + 10));
	memset(neg, 0, sizeof(int) * (n + 10));
	queue<int>que;
	que.push(st);
	dis[st] = 0;
	inq[st] = true;
	while (!que.empty()) {
		int u = que.front(); que.pop();
		inq[u] = false;
		for (int i = head[u]; ~i; i = e[i].next) {
			int v = e[i].to;
			if (dis[v] > dis[u] + e[i].w) {
				dis[v] = dis[u] + e[i].w;
				if (!inq[v]) {
					que.push(v);
					inq[v] = true;
					++neg[v];
					if (neg[v] > n) {
						return true;
					}
				}
			}
		}
	}
	return false;
}
int main() {
#ifdef _DEBUG
	FILE* _INPUT = freopen("input.txt", "r", stdin);
	//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif // !_DEBUG
	int cas = 0, T;
//	rd(T);
//	while(T--){
	while (scanf("%d", &n),n) {
		scanf("%d", &m);
		memset(head, -1, sizeof(int) * (n + 10)); cntE = 0;
		while (m--) {
			int l, r, w; char op[5];
			scanf("%d %d %s %d", &l, &r, op, &w);
			if (op[0] == 'g') add(r + l, l - 1, -w - 1);
			else add(l - 1, r + l, w - 1);
		}
		for (int i = 0; i <= n; ++i) {
			add(n + 1, i, 0);
		}
		if (!spfa(++n)) puts("lamentable kingdom");
		else puts("successful conspiracy");
	}
	return 0;
}

THE MATRIX PROBLEM

HDU-3666
这道题通过题目得出的不等式如下:
L ≤ C i j ∗ a i b j ≤ U L\le \frac{C_{ij}*a_i}{b_j}\le U LbjCijaiU
这不等式并不符合差分约束的加减法形式,那就将他转换成加减法,对上面的式子取对数,最后转换成
ln ⁡ ( L C i j ) ≤ ln ⁡ ( a i ) − ln ⁡ ( b j ) ≤ ln ⁡ ( U C i j ) \ln(\frac{L}{C_{ij}})\le \ln(a_i)-\ln(b_j)\le \ln(\frac{U}{C_{ij}}) ln(CijL)ln(ai)ln(bj)ln(CijU)
通过这个不等式建边即可
另外这题使用朴素版的 s p f a spfa spfa 会超时,需要对 s p f a spfa spfa 进行一些优化
优化的方法有两种: S L F SLF SLF L L L LLL LLL 这篇博客讲得不错
这题使用 S L F SLF SLF 就足够了
建边的时候还需要加一个超级源点,连接每个 i i i

#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <set>
#include <cstdlib>
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uii;
typedef pair<int, ll> pii;
template<typename T>
inline void rd(T& x)
{
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
int gcd(int a, int b) {
	if (b == 0) return a;
	return gcd(b, a % b);
}
const int N = 2e5 + 10;
const int M = 1e7 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-5;
int sgn(double x) {
	if (fabs(x) < eps) return 0;
	if (x > 0) return 1;
	return -1;
}
int n, m, st, ed, k;
int head[N], cntE;
struct edge {
	int to, next;
	double w;
}e[M];
void add(int u, int v,double w = 0) {
	e[cntE].to = v;
	e[cntE].next = head[u];
	e[cntE].w = w;
	head[u] = cntE++;
}
bool inq[N];
double dis[N];
int neg[N];
bool spfa(int st) {
	memset(inq, false, sizeof(bool) * (n + m + 10));
	memset(dis, inf, sizeof(double) * (n + m + 10));
	memset(neg, 0, sizeof(int) * (n + m + 10));
	deque<int>que;
	que.push_back(st);
	dis[st] = 0;
	inq[st] = true;
	while (!que.empty()) {
		int u = que.front(); que.pop_front();
		inq[u] = false;
		for (int i = head[u]; ~i; i = e[i].next) {
			int v = e[i].to;
			if (dis[v] > dis[u] + e[i].w) {
				dis[v] = dis[u] + e[i].w;
				if (!inq[v]) {
					inq[v] = true;
					if (!que.empty() && dis[v] < dis[que.front()]) que.push_front(v);
					else que.push_back(v);
					++neg[v];
					if (neg[v] >= n + m) return false;
				}
			}
		}
	}
	return true;
}
int main() {
#ifdef _DEBUG
	FILE* _INPUT = freopen("input.txt", "r", stdin);
	//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif // !_DEBUG
	int cas = 0, T;
//	rd(T);
//	while (T--) {
	while (~scanf("%d %d", &n,&m)) {
		int L, U; rd(L), rd(U);
		memset(head, -1, sizeof(int) * (n + m + 10)); cntE = 0;
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= m; ++j) {
				int x; rd(x);
				add(n + j, i, log(1.0 * U / x));
				add(i, n + j, -log(1.0 * L / x));
			}
			add(0, i, 0);
		}
		if (spfa(0)) puts("YES");
		else puts("NO");
	}
	return 0;
}

Is the Information Reliable?

POJ-2983
这道题有两种关系:

  • P: S A − S B = x S_A-S_B=x SASB=x 转化为 x ≤ S A − S B ≤ x x\le S_A-S_B\le x xSASBx
  • V: S A − S B ≥ 1 S_A-S_B\ge 1 SASB1
  • 建立一个超级源点

直接建图就行了

#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <set>
#include <cstdlib>
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uii;
typedef pair<int, ll> pii;
template<typename T>
inline void rd(T& x)
{
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
int gcd(int a, int b) {
	if (b == 0) return a;
	return gcd(b, a % b);
}
const int N = 2e5 + 10;
const int M = 1e7 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-5;
int sgn(double x) {
	if (fabs(x) < eps) return 0;
	if (x > 0) return 1;
	return -1;
}
int n, m, st, ed, k;
int head[N], cntE;
struct edge {
	int to, next;
	int w;
}e[M];
void add(int u, int v, int w = 0) {
	e[cntE].to = v;
	e[cntE].next = head[u];
	e[cntE].w = w;
	head[u] = cntE++;
}
bool inq[N];
int dis[N];
int neg[N];
bool spfa(int st) {
	memset(inq, false, sizeof(bool) * (n + 10));
	memset(dis, -inf, sizeof(int) * (n + 10));
	memset(neg, 0, sizeof(int) * (n + 10));
	deque<int>que;
	que.push_back(st);
	dis[st] = 0;
	inq[st] = true;
	while (!que.empty()) {
		int u = que.front(); que.pop_front();
		inq[u] = false;
		for (int i = head[u]; ~i; i = e[i].next) {
			int v = e[i].to;
			if (dis[v] < dis[u] + e[i].w) {
				dis[v] = dis[u] + e[i].w;
				if (!inq[v]) {
					inq[v] = true;
					if (!que.empty() && dis[v] > dis[que.front()]) que.push_front(v);
					else que.push_back(v);
					++neg[v];
					if (neg[v] >= n) return false;
				}
			}
		}
	}
	return true;
}
int main() {
#ifdef _DEBUG
	FILE* _INPUT = freopen("input.txt", "r", stdin);
	//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif // !_DEBUG
	int cas = 0, T;
	//	rd(T);
	//	while (T--) {
	while (~scanf("%d %d", &n, &m)) {
		memset(head, -1, sizeof(int) * (n + 10)); cntE = 0;
		while (m--) {
			char op; scanf(" %c", &op);
			if (op == 'P') {
				int x, y, w; rd(x), rd(y), rd(w);
				add(y, x, w);
				add(x, y, -w);
			}
			else {
				int x, y; rd(x), rd(y);
				add(y, x, 1);
			}
		}
		for (int i = 1; i <= n; ++i) add(0, i, 0);
		if (spfa(0)) puts("Reliable");
		else puts("Unreliable");
	}
	return 0;

Cashier Employment

Cashier Employment HDU - 1529
这道题偏难,建立的不等式关系如下:

  • 设所有的申请人当中,在 x x x 时间点入职的数量为 c n t x cnt_x cntx, S i S_i Si 为 0 ~ i 的总申请人数量,其中 S 0 = 0 S_0=0 S0=0 , S 1 = S 0 + R [ 0 ] S_1=S_0+R[0] S1=S0+R[0] , S i = S i − 1 + R [ i − 1 ] S_i=S_{i-1}+R[i-1] Si=Si1+R[i1]
  • 0 ≤ S i + 1 − S i ≤ c n t i 0\le S_{i+1}-S_{i}\le cnt_i 0Si+1Sicnti,表示在第 i i i 个时间点最多有 c n t i cnt_i cnti 个人入职
  • 对于 i ≥ 8 i\ge8 i8 有: S i − S i − 8 ≥ R [ i − 1 ] S_i-S_{i-8}\ge R[i-1] SiSi8R[i1],表示在第 i i i 个时间点至少有 R [ i − 1 ] R[i-1] R[i1] 个人在岗
  • 对于 i < 8 i\lt8 i<8 有: S i − S 16 + i + S 24 ≥ R [ i − 1 ] S_i-S_{16+i}+S_{24}\ge R[i-1] SiS16+i+S24R[i1],其中 S 24 S_{24} S24 为申请总人数,即为答案。
  • 可以二分答案,确定一个中值 m i d mid mid ,令 S 24 = m i d S_{24}=mid S24=mid ,另外需要加个限制
    m i d ≤ S 24 ≤ m i d mid\le S_{24}\le mid midS24mid
  • 接下来建边跑 s p f a spfa spfa 即可
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <set>
#include <cstdlib>
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uii;
typedef pair<int, ll> pii;
template<typename T>
inline void rd(T& x)
{
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
int gcd(int a, int b) {
	if (b == 0) return a;
	return gcd(b, a % b);
}
const int N = 2e5 + 10;
const int M = 1e7 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-5;
int sgn(double x) {
	if (fabs(x) < eps) return 0;
	if (x > 0) return 1;
	return -1;
}
int n, m, st, ed, k;
int head[N], cntE;
struct edge {
	int to, next;
	int w;
}e[M];
void add(int u, int v, int w = 0) {
	e[cntE].to = v;
	e[cntE].next = head[u];
	e[cntE].w = w;
	head[u] = cntE++;
}
bool inq[N];
int dis[N];
int neg[N];
bool spfa(int st) {
	memset(inq, false, sizeof(bool) * (n + 10));
	memset(dis, inf, sizeof(int) * (n + 10));
	memset(neg, 0, sizeof(int) * (n + 10));
	deque<int>que;
	que.push_back(st);
	dis[st] = 0;
	inq[st] = true;
	while (!que.empty()) {
		int u = que.front(); que.pop_front();
		inq[u] = false;
		for (int i = head[u]; ~i; i = e[i].next) {
			int v = e[i].to;
			if (dis[v] > dis[u] + e[i].w) {
				dis[v] = dis[u] + e[i].w;
				if (!inq[v]) {
					inq[v] = true;
					if (!que.empty() && dis[v] < dis[que.front()]) que.push_front(v);
					else 
						que.push_back(v);
					++neg[v];
					if (neg[v] >= n) return false;
				}
			}
		}
	}
	return true;
}
int R[N], a[N];
bool check(int x) {
	memset(head, -1, sizeof(int) * (24 + 10)); cntE = 0;
	for (int i = 0; i < 24; ++i) {
		add(i, i + 1, a[i]);
		add(i + 1, i, 0);
	}
	for (int i = 8; i < 24; ++i) {
		add(i, i - 8, -R[i - 1]);
	}
	for (int i = 1; i < 8; ++i) {
		add(i, i + 16, x - R[i - 1]);
	}
	add(0, 24, x);
	add(24, 0, -x);
	if (spfa(0)) return true;
	return false;
}
int main() {
#ifdef _DEBUG
	FILE* _INPUT = freopen("input.txt", "r", stdin);
//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif // !_DEBUG
	int cas = 0, T;
	rd(T);
	while (T--) {
//	while (~scanf("%d %d", &k, &m)) {
		n = 24;
		memset(a, 0, sizeof(int) * (n + 10));
		for (int i = 0; i < 24; ++i) rd(R[i]);
		rd(m);
		for (int i = 1; i <= m; ++i) {
			int x; rd(x);
			++a[x];
		}
		int l = 0, r = m;
		while (l <= r) {
			int mid = l + r >> 1;
			if (check(mid))
				r = mid - 1;
			else l = mid + 1;
		}
		if (r + 1 > m) puts("No Solution");
		else printf("%d\n", r + 1);
	}
	return 0;
}


Spy’s Work-HDU4274

中等题

  • 首先需要满足的条件是 父节点的权值 ≥ \ge 所有子结点的权值+1
  • 所以为了满足上述条件,需要对所有点建立一个 d f s dfs dfs 序,来构成上述不等式
  • 另外 S i + 1 − S i ≥ 1 S_{i+1}-S_{i}\ge1 Si+1Si1
  • 最后建边跑 s p f a spfa spfa 即可
  • 代码 m i n n [ i ] minn[i] minn[i] 表示第 i i i 个点为根的子树中的最小结点 d f s dfs dfs 序号, i d [ i ] id[i] id[i] 表示该点的 d f s dfs dfs 序号
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <set>
#include <cstdlib>
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uii;
typedef pair<int, ll> pii;
template<typename T>
inline void rd(T& x)
{
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
int gcd(int a, int b) {
	if (b == 0) return a;
	return gcd(b, a % b);
}
const int N = 2e5 + 10;
const int M = 1e7 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-5;
int sgn(double x) {
	if (fabs(x) < eps) return 0;
	if (x > 0) return 1;
	return -1;
}
int n, m, st, ed, k;
int head[N], cntE;
struct edge {
	int to, next;
	int w;
}e[M];
void add(int u, int v, int w = 0) {
	e[cntE].to = v;
	e[cntE].next = head[u];
	e[cntE].w = w;
	head[u] = cntE++;
}
bool inq[N];
int dis[N];
int neg[N];
bool spfa(int st) {
	memset(inq, false, sizeof(bool) * (n + 10));
	memset(dis, -inf, sizeof(int) * (n + 10));
	memset(neg, 0, sizeof(int) * (n + 10));
	deque<int>que;
	que.push_back(st);
	dis[st] = 1;
	inq[st] = true;
	while (!que.empty()) {
		int u = que.front(); que.pop_front();
		inq[u] = false;
		for (int i = head[u]; ~i; i = e[i].next) {
			int v = e[i].to;
			if (dis[v] < dis[u] + e[i].w) {
				dis[v] = dis[u] + e[i].w;
				if (!inq[v]) {
					inq[v] = true;
					if (!que.empty() && dis[v] > dis[que.front()]) que.push_front(v);
					else 
						que.push_back(v);
					++neg[v];
					if (neg[v] >= n) return false;
				}
			}
		}
	}
	return true;
}
int c[N], id[N], num, minn[N];
bool vis[N];
vector<int>vec[N];
void dfs(int x) {
	vis[x] = true;
	minn[x] = inf;
	for (auto v : vec[x]) {
		dfs(v);
		minn[x] = min(minn[x], minn[v]);
	}
	minn[x] = min(minn[x], id[x] = ++num);
}
int main() {
#ifdef _DEBUG
	FILE* _INPUT = freopen("input.txt", "r", stdin);
//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif // !_DEBUG
	int cas = 0, T;
//	rd(T);
//	while (T--) {
	while (~scanf("%d", &n)) {
		memset(head, -1, sizeof(int) * (n + 10)); cntE = 0;
		for (int i = 1; i <= n; ++i) vec[i].clear();
		for (int i = 2; i <= n; ++i) {
			int x; rd(x);
			vec[x].push_back(i);
		}
		memset(vis, false, sizeof(bool) * (n + 10)); num = 0;
		for (int i = 1, pre = num; i <= n; ++i) {
			if (!vis[i]) {
				dfs(i);
			}
		}
		rd(m);
		while (m--) {
			int x, w; char op;
			scanf("%d %c %d", &x, &op, &w);
			if (op == '<') add(id[x], minn[x] - 1, 1 - w);
			else if (op == '>') add(minn[x] - 1, id[x], w + 1);
			else {
				add(minn[x] - 1, id[x], w);
				add(id[x], minn[x] - 1, -w);
			}
		}
		for (int i = 1; i <= n; ++i) add(i - 1, i, 1);
		if (spfa(0)) puts("True");
		else puts("Lie");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值