题解:P7624 [AHOI2021初中组] 地铁
题意:一个环,给出区间 \([l,r]\) 的长度 \(\ge x,\le x\) 的限制,求环的长度有多少种不同的。
做法:
首先环形不太好处理,我们不如改为枚举一个 \(L\) 然后进行判定。
首先肯定要有 \(L \ge dis_n+1\),然后对于每个 \(l<r\),要求 \(dis_r - dis_l \ge x\) 或 \(dis_r-dis_l \le x\),对于每个 \(l > r\),我们有 \(dis_r - dis_l\le L - x\) 或 \(dis_r-dis_l \ge L - x\),其中 \(dis_i\) 代表 \(1 \rightarrow i\) 的距离。
那么直接跑差分约束就可以判定了,但是答案可能很多,我们不可能每个都检验,如何处理?
考虑到合法的 \(L\) 一定是连续的,因为这个限制都是线性的,最后得出来对 \(L\) 的限制肯定也是一个区间。
那么我们考虑二分右端点和左端点,这里以求左端点为例,当我们找到一个判定成功的点是很好决定向左继续查找的,但是如果我们判定不成功就不知道往左还是往右了,怎么处理?
我们考虑不等式中 \(L\) 的系数,当我们找到一个负环的时候,看一下负环上 \(L\) 的总系数 \(k\),如果 \(k<0\),那么意味着如果我们的 \(L\) 更大时这个负环仍然存在,所以就要向左查询;如果 \(k=0\),那么意味着无论 \(L\) 这个负环都会存在,那么直接无解了;如果 \(k>0\),那么 \(k\) 更大才可能消掉负环,向右查询。
然后对于无限解,我们考虑判定一个大数 \(\infty\) 能否可行,如果可行就认为有无限解。
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 1e3 + 5;
int n, m, c;
struct Value {
int k, b;
friend Value operator+(Value x, Value y) {
return Value{x.k + y.k, x.b + y.b};
}
friend Value operator-(Value x, Value y) {
return Value{x.k - y.k, x.b - y.b};
}
friend bool operator<(Value x, Value y) {
return x.k * c + x.b < y.k * c + y.b;
}
friend bool operator>(Value x, Value y) {
return y < x;
}
} dis[maxn];
struct Edge {
int to;
Value val;
};
vector<Edge> e[maxn];
int cnt[maxn], vis[maxn], pre[maxn], p[maxn];
int getcircle(int v) {
for (int i = 1; i <= n; i++)
vis[i] = 0;
int tot = 0, cnt = 0;
while(!vis[v]) {
p[++tot] = v;
vis[v] = 1;
v = pre[v];
}
for (int i = 1; i <= tot; i++)
cnt += dis[p[i]].k;
return cnt;
}
int spfa(int x) {
c = x;
for (int i = 1; i <= n; i++)
dis[i] = {0, (int)(9e18)};
dis[1] = {0, 0};
queue<int> q;
memset(vis, 0, sizeof(vis));
memset(cnt, 0, sizeof(cnt));
q.push(1);
while(!q.empty()) {
int tmp = q.front();
q.pop();
vis[tmp] = 0;
for (int i = 0; i < e[tmp].size(); i++) {
Edge v = e[tmp][i];
if(dis[v.to] > dis[tmp] + v.val) {
dis[v.to] = dis[tmp] + v.val;
pre[v.to] = tmp;
if(!vis[v.to]) {
vis[v.to] = 1;
q.push(v.to);
cnt[v.to]++;
if(cnt[v.to] > n + 1) {
int t = getcircle(v.to);
return t > 0 ? 1 : - 1;
}
}
}
}
}
return 0;
}
signed main() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int tp, x, y, w;
cin >> tp >> x >> y >> w;
if(tp == 1) {
if(x < y)
e[x].push_back(Edge{y, Value{0, -w}});
else
e[x].push_back(Edge{y, Value{1, -w}});
}
else {
if(x < y)
e[y].push_back(Edge{x, Value{0, w}});
else
e[y].push_back(Edge{x, Value{-1, w}});
}
}
for (int i = 1; i < n; i++)
e[i].push_back(Edge{i + 1, Value{0, -1}});
e[n].push_back(Edge{1, Value{1, -1}});
int l = -1, r = (1e12 + 5);
while(l + 1 < r) {
int mid = l + r >> 1;
int t = spfa(mid);
if(t == 0)
l = mid;
else if(t == 1)
l = mid;
else
r = mid;
}
if(l >= (int)(1e12)) {
cout << -1 << endl;
return 0;
}
int rr = l;
l = -1, r = (1e12 + 5);
while(l + 1 < r) {
int mid = l + r >> 1;
int t = spfa(mid);
if(t == 0)
r = mid;
else if(t == 1)
l = mid;
else
r = mid;
}
cout << rr - r + 1 << endl;
return 0;
}

浙公网安备 33010602011771号