关于差分约束的相关定义,这篇博客推荐看一下 何为差分约束
接下来通过些题目来实战一下
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
SB−SA≤w
对于MD个关系,有
S
B
−
S
A
≥
w
S_B-S_A\ge w
SB−SA≥w
其中
S
i
S_i
Si 表示
i
i
i 到原点的距离。由于题目还有另外两种情况,
- 无解则输出-1,无解便是跑spfa出现负圈的情况
- 无穷大则输出-2,便是通过以上关系并不能更新到第n个点,则说明第n点不受上述条件的限制。
这题算是差分约束的入门题,只有两种基本的关系,在写代码的时候需要进行下转换,使所有式子的不等于号方向统一:
- 仅有 S B − S A ≤ w S_B-S_A\le w SB−SA≤w 与 S A − S B ≤ − w S_A-S_B\le -w SA−SB≤−w ,这个时候相当于求最短路
- 仅有 S A − S B ≥ − w S_A-S_B\ge -w SA−SB≥−w 与 S B − S A ≥ w S_B-S_A\ge w SB−SA≥w ,这个时候相当于求最长路
代码如下,选取了求最短路的情况:
#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+1−Si≥1
- 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
SR−SL−1≥x
所以本题的不等式关系有:
- S R − S L − 1 ≥ x S_R-S_{L-1}\ge x SR−SL−1≥x 这个是题目输入的关系。
- 另外还有隐藏关系,第 i i i 数可选可不选,因此有 0 ≤ S i + 1 − S i ≤ 1 0\le S_{i+1}-S_{i}\le1 0≤Si+1−Si≤1
将不等式符号转换成同一方向建边跑 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
a1到ai 的前缀和。有
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(si−1)<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(si−1)>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
L≤bjCij∗ai≤U
这不等式并不符合差分约束的加减法形式,那就将他转换成加减法,对上面的式子取对数,最后转换成
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 SA−SB=x 转化为 x ≤ S A − S B ≤ x x\le S_A-S_B\le x x≤SA−SB≤x
- V: S A − S B ≥ 1 S_A-S_B\ge 1 SA−SB≥1
- 建立一个超级源点
直接建图就行了
#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=Si−1+R[i−1]
- 有 0 ≤ S i + 1 − S i ≤ c n t i 0\le S_{i+1}-S_{i}\le cnt_i 0≤Si+1−Si≤cnti,表示在第 i i i 个时间点最多有 c n t i cnt_i cnti 个人入职
- 对于 i ≥ 8 i\ge8 i≥8 有: S i − S i − 8 ≥ R [ i − 1 ] S_i-S_{i-8}\ge R[i-1] Si−Si−8≥R[i−1],表示在第 i i i 个时间点至少有 R [ i − 1 ] R[i-1] R[i−1] 个人在岗
- 对于 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] Si−S16+i+S24≥R[i−1],其中 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 mid≤S24≤mid - 接下来建边跑 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+1−Si≥1
- 最后建边跑 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;
}