题解 [UR #2] 跳蚤公路
赛时见到这种题就快走吧,剩下的题还有机会
就现在我这份码都基本是贺的
首先发现有发财路径等价于有负环
- 当题目要给一些东西定/赋权值,在不同的物品上贡献有正有负/有不同的系数:
若你定的权是 \(x\),那这个物品的权值可以写成 \(kx+b\) 的形式吗?
一个暴力是枚举简单环
那么整个环的权值是 \((\sum k)x+\sum b\),可以以此解出这个环是负环时 \(x\) 的范围
那么枚举这个环能到的点更新答案即可
然后优化一下是发现对于 \(\sum k\) 相同的点,只有 \(\sum b\) 最小的点是有用的
所以令 \(f_{i, j, k}\) 为从 \(i\) 出发到 \(j\),\(\sum k=k\) 时的 \(\min\{\sum b\}\)
用 Floyd 可以 \(O(n^5)\) 做
再优化一下就是优化解范围的过程了
bellman-ford 和 spfa 同样可以处理负环
令 \(f_{i, j, k}\) 为从 1 出发,走了不超过 \(i\) 条边,到点 \(j\),经过的边 \(\sum k=k\) 时的 \(\min\{\sum b\}\)
转移是 \(O(n^3)\) 的
但是这样怎么解范围呢?
在 bellman-ford 中有负环等价于 \(dis_{n, v}<dis_{n-1, v}\)
令 \(g_{i, j}\) 为走了不超过 \(i\) 条边,到点 \(j\) 的最短路
则 \(g_{i, j}=\min f_{i, j, k}\)
则点 \(v\) 在某个负环上等价于 \(g_{n, v}<g_{n-1, v}\)
即
\[\min \{ kx + f[n][v][k] \} \geqslant \min \{ jx + f[n - 1][v][j] \}
\]
于是现在要解这个东西
- 解一类形如 \(\min \{ kx + f_k \} \geqslant \min \{ jx + g_j \}\) 的不等式:
一个通法做法是枚举钦定最小值在 \(j, k\) 处取到,再解外层不等式,最后取交
听说能凸包优化到一个 log ?
还有一个做法见题解
正确性的话取并相当于可行范围取并了,也就是右边的 min
取交同理相当于左边取 max
感性理解,逃
那么可行范围也有了
现在要给一车范围取交取并……
- 当题目要求给一车范围取交取并(尤其在它是什么东西的合法范围时)尝试证明答案一定是 \((-\infty,t], [l,r]\) 或 \([t,+\infty)\) 的形式
如果真的是会省好多事虽然还是很麻烦
写的时候可以来回补集转化
对于本题
于是写码的话可以利用上面这个性质优化(找到一个合法区间直接输出就行)
复杂度 \(O(n^4)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
#define N 110
#define fir first
#define sec second
#define pb push_back
#define ll long long
#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m;
bool g[N][N];
const int dlt=105;
int f[N][N][N<<1];
#define f(a, b, c) f[a][b][c+dlt]
int head[N], ecnt;
vector<pair<int, int>> s[N], sta;
struct edge{int from, to, next, k, b;}e[20010];
inline void add(int s, int t, int k, int b) {e[++ecnt]={s, t, head[s], k, b}; head[s]=ecnt;}
signed main()
{
n=read(); m=read();
memset(head, -1, sizeof(head));
memset(f, 0x3f, sizeof(f));
for (int i=1,u,v,w,s; i<=m; ++i) {
u=read(); v=read(); w=read(); s=read();
add(u, v, s, w); g[u][v]=1;
}
for (int i=1; i<=n; ++i) add(i, i, 0, 0);
for (int i=1; i<=n; ++i) g[i][i]=1;
for (int k=1; k<=n; ++k)
for (int i=1; i<=n; ++i)
for (int j=1; j<=n; ++j)
g[i][j]|=g[i][k]&g[k][j];
f(0, 1, 0)=0;
for (int i=0; i<n; ++i) {
for (int j=1; j<=ecnt; ++j) {
int u=e[j].from, v=e[j].to, s=e[j].k, w=e[j].b;
for (int k=-n; k<=n; ++k) if (f(i, u, k)!=INF) {
// cout<<i+1<<' '<<v<<' '<<k+s<<' '<<f(i, u, k)+w<<endl;
f(i+1, v, k+s)=min(f(i+1, v, k+s), f(i, u, k)+w);
}
}
}
for (int i=1; i<=n; ++i) {
for (int k=-n; k<=n; ++k) if (f(n, i, k)!=INF) {
int l=-INF, r=INF;
for (int j=-n; j<=n; ++j) if (f(n-1, i, j)!=INF) {
// cout<<"kj: "<<k<<' '<<j<<endl;
// cout<<"f: "<<f(n, i, k)<<' '<<f(n-1, i, j)<<endl;
if (k>j) r=min(r, (ll)ceil((1.0L*f(n-1, i, j)-f(n, i, k))/(1.0L*k-j)));
else if (k<j) l=max(l, (ll)floor((1.0L*f(n-1, i, j)-f(n, i, k))/(1.0L*k-j)));
else if (f(n, i, k)>=f(n-1, i, j)) goto jump1;
}
if (l<r) s[i].pb({l, r});
jump1: ;
}
// sort(s[i].begin(), s[i].end());
// cout<<"i: "; for (auto it:s[i]) cout<<"("<<it.fir<<','<<it.sec<<") "; cout<<endl;
}
for (int i=1; i<=n; ++i) {
// cout<<"i: "<<i<<endl;
sta.clear();
for (int j=1; j<=n; ++j) if (g[1][j]&&g[j][i])
for (auto it:s[j]) sta.pb(it);
sort(sta.begin(), sta.end());
int lst=-INF;
for (int j=0; j<sta.size(); ++j) {
if (!j && sta[j].fir!=-INF) {puts("-1"); goto jump2;}
if (lst!=-INF && lst<=sta[j].fir) {printf("%lld\n", (sta[j].fir-lst+1)>1e18?-1:sta[j].fir-lst+1); goto jump2;}
lst=max(lst, sta[j].sec);
}
puts(lst==INF?"0":"-1");
jump2: ;
}
return 0;
}