*题解:P7831 [CCO 2021] Travelling Merchant
解析
首先无解的点可以通过持续删出度为 \(0\) 的点来去除掉。
考虑 DP。让点代表状态,设 \(f_{i}\) 表示从点 \(i\) 出发的答案。让边代表转移,对于从 \(i\) 连向 \(j\) 的边 \(e\),有转移 \(f_{i} \leftarrow \min(f_{i},\max(f_j - p_e,r_e))\)。于是可以发现,初始答案上界为 \(\max r\)。若 \(r\) 最大的边是从 \(u\) 出发连向 \(v\) 的边 \(e\),则转移时 \(\max(f_v - p_e,r_e)\) 恰好能取到这个上界,我们也就不需要考虑 \(f_v\) 对转移造成的影响了,可以把这条边删掉。当 \(u\) 的入度为 \(0\),此时 \(f_u\) 的值已经确定,故可以去转移连接 \(u\) 的那些点,同样转移后把边删掉。一直这样转移直到不存在出度为 \(0\) 的点,然后继续取剩下的边中 \(r\) 最大的来转移并删掉,再让出度为 \(0\) 的点去转移别的点,重复此过程。
为什么跑完一轮之后取剩下边当中 \(r\) 最大的边 \(e\) 是对的?因为不存在出度为 \(0\) 的点保证了从任意一个点出发都必定能走到一个环,而由于资产单调不减,故取剩下边中的 \(\max r\) 就拥有了通过任意边的资格。
整个过程可以通过建反图拓扑排序来实现。
时间复杂度 \(O(m\log m)\)。
想到了的地方 / 考虑了的方案
找无解点的方法。
二分答案。
缩点。
没想到的地方
DP。
为什么没想到 / 怎么想到
发现图上有环且缩点做不了就否掉了 DP 做法。
代码
/*
最终应当是攒够钱后找个环走
对于每个点,找一个带环的路径使得到达路径上的边时,已经经过的边的 p 之和减去当前 r 的最小值最大
*/
#include <bits/stdc++.h>
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1)
using namespace std;
typedef long long ll;
typedef pair<ll,int> pii;
const int N = 2e5 + 5,M = 20 + 5,mod1 = 998244353,mod2 = 1e9 + 7,base1 = 131,base2 = 13331;
struct Edge{
int a,b,r,p,id;
friend bool operator > (Edge e1,Edge e2){
return e1.r > e2.r;
}
}e[N];
ll f[N];
int ind[N],rev[N];
bool vis[N];
vector<int> g[N],g2[N];
int n,m;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
memset(f,127,sizeof(f));
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>e[i].a>>e[i].b>>e[i].r>>e[i].p;
e[i].id = i;
ind[e[i].a]++;
g[e[i].b].push_back(i);
g2[e[i].a].push_back(e[i].b);
}
sort(e + 1,e + m + 1,greater<Edge>());
for(int i=1;i<=m;i++){
rev[e[i].id] = i;
}
for(int i=1;i<=n;i++){
for(int &j : g[i]){
j = rev[j];
}
}
queue<int> q;
for(int i=1;i<=n;i++){
if(!ind[i]){
q.push(i);
}
}
int pos = 1;
while(1){
while(q.empty()){
while(vis[pos]) pos++;
if(pos > m) break;
int u = e[pos].a,r = e[pos].r;
vis[pos] = true;
f[u] = min(f[u],1ll * r);
ind[u]--;
if(!ind[u]){
q.push(u);
}
}
if(q.empty()) break;
int u = q.front();
q.pop();
for(int i : g[u]){
int v = e[i].a,r = e[i].r,p = e[i].p,id = e[i].id;
if(vis[rev[id]]) continue;
vis[rev[id]] = true;
ind[v]--;
if(f[u] < 9e18){
f[v] = min(f[v],max(1ll * r,f[u] - p));
}
if(!ind[v]){
q.push(v);
}
}
}
for(int i=1;i<=n;i++){
cout<<(f[i] < 9e18 ? f[i] : -1)<<" ";
}
return 0;
}

浙公网安备 33010602011771号