这是一篇良心的题解
这道题不打开标签还是能看出是差分约束的,但是和板子的差分约束有点不同,它的不等式中未知项不止两个,但是,这些未知项都是连续的,自然我们不难想到用前缀和的形式来表示。
关于翻译错误
仔细看一下原文的题面是能够看出来的,应该是:
a
x
+
a
x
+
1
+
…
+
a
x
+
y
a_x + a_{x + 1} + … + a_{x + y}
ax+ax+1+…+ax+y
最长路判正环的SPFA
以 g t gt gt 为例:
所以我们可以把它转化为:
s
u
m
x
+
y
−
s
u
m
x
−
1
>
c
sum_{x + y} - sum_{x - 1} > c
sumx+y−sumx−1>c
s
u
m
x
+
y
>
s
u
m
x
−
1
+
c
sum_{x + y} > sum_{x - 1} + c
sumx+y>sumx−1+c
熟悉差分约束的同学应该就能发现这和最长路的三角形不等式就有点像了,但是还差一个等号。因为这些值都是整数,所以,我们在不等式右边加上一个一,不等式就能取到等号了:
s
u
m
x
+
y
≥
s
u
m
x
−
1
+
c
+
1
sum_{x + y} \geq sum_{x - 1} + c + 1
sumx+y≥sumx−1+c+1
同理:
对于
l
t
lt
lt 也能得到:
s
u
m
x
−
1
>
s
u
m
x
+
y
−
c
sum_{x - 1} > sum_{x + y} - c
sumx−1>sumx+y−c
s
u
m
x
−
1
≥
s
u
m
x
+
y
−
c
+
1
sum_{x - 1} \geq sum_{x + y} - c + 1
sumx−1≥sumx+y−c+1
现在,根据这两个式子应该就很容易连边和跑SPFA了。
关于一些小技巧容易出现的小错误
小技巧
因为不知道源点是谁,所以最开始可以将所有点都入队,就不用再建一个超级源点,能节省一点时间和空间,也不容易出错。
关于正确性
因为所有的点都具有扩展的潜力,所以把所有点入队并不影响答案,而平时跑最短路的SPFA之所以只把源点入队,是因为一开始只能从源点开始扩展才是有意义的,而在差分约束中,则不存在先后的问题,这也是SPFA基于DP思想的好处。
小错误
- 因为会有 s u m 0 sum_0 sum0 的存在,也就是存在零号节点,所以在入队和初始化的时候不要忘了它,当然也可以用memset,但之前被卡了一次memset之后不太敢用了,做这道题的时候因为漏掉了零号节点,就调了很久。
- 前向星的初始化千万不要漏掉边集数组的指针。
然后就得到了Rank1的代码
#include<cstdio>
#include<queue>
const int N = 1e5 + 5;
const int M = 1e5 + 5;
struct edge {
int next,to,w;
}a[M];
int head[N],a_size,n,m,dis[N],cnt[N]; bool vis[N];
inline void add(int u,int v,int w) {
a[++a_size] = (edge){head[u],v,w};
head[u] = a_size;
}
bool SPFA() {
std::queue<int> q;
for(int i = 0; i <= n; i++)
q.push(i),vis[i] = true;
while(!q.empty()) {
int x = q.front();
if(cnt[x] > n + 1) return false;
q.pop(); vis[x] = false;
for(int i = head[x]; i; i = a[i].next) {
int y = a[i].to,w = a[i].w;
if(dis[y] < dis[x] + w) {
dis[y] = dis[x] + w;
cnt[y] = cnt[x] + 1;
if(!vis[y]) q.push(y),vis[y] = true;
}
}
}
return true;
}
inline int read() {
int x = 0,flag = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')flag = -1;ch = getchar();}
while(ch >='0' && ch <='9'){x = (x << 3) + (x << 1) + ch - 48;ch = getchar();}
return x * flag;
}
int main() {
while(true) {
n = read(); if(n == 0) break;
m = read(); a_size = 1;
for(int i = 0; i <= n; i++)
head[i] = 0,dis[i] = -1e7,cnt[i] = 0,vis[i] = false;
for(int i = 1; i <= m; i++) {
int x = read(),y = read();
char ord[5]; scanf("%s",ord);
if(ord[0] == 'g') {
int c = read();
add(x - 1,x + y,c + 1);
}
else {
int c = read();
add(x + y,x - 1,1 - c);
}
}
SPFA() ? puts("lamentable kingdom") : puts("successful conspiracy");
}
return 0;
}
差分约束好题
(SCOI)糖果
囊括了差分约束的常见限制条件,以及最小正整数解的求法。