题解: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;
}
posted @ 2025-07-23 08:49  LUlululu1616  阅读(68)  评论(0)    收藏  举报