一道看一眼就知道差分约束的题目
但是最短路spfa的时候注意松弛条件是
if(dis[u]+w[i]<dis[v[i]]) dis[v[i]]=dis[u]+w[i];
不能写成
if(dis[u]+w[i]<=dis[v[i]]) dis[v[i]]=dis[u]+w[i]
否则会TLE
就是如何把\( a_{i}-a_{j} \ge t \),方法非常简单,只需要乘上-1 就可以愉快地变成\( a_{j}-a_{i} \le -t \)了
然后就是建立图。
瞪眼法可知我们可以使用类似前缀和的结构来维护一个到第i个位置一共种了多少颗树的信息
然后我们设\( d_{i} \) 表示到第i个位置现在有多少颗树
题目中要求的条件
B到E中间种的树的数量不少于T棵
就可以转化为 \( d_{e}-d_{b-1} \ge t \)了,然后用到刚才的转化方法
把式子化成 \( d_{b-1}-d_{e} \le -t \)了
所以我们可以从e向b-1连一条边权为-t的边
建完了这些边之后,我们会发现一个问题,这个图没办法直接跑最短路解出解废话因为这个图根本不联通啊
所以我们要来思考一下还有什么其他的约束条件
首先显而易见的有
\( d_{n+1}-d_{i} \le 0 \ ,0 \le i \ge n \) 即 \( d_{i}-d_{n+1} \ge 0 \ ,0 \le i \le n \) ,所以我们可以从n+1的节点连一条边权为0的边到第i个节点
其次还有几个显而易见的结论
\(d_{i}-d_{i-1} \ge 1 \)
\(d_{i-1} - d_{i} \ge 0\)
同样的从i-1向i连一条边权为1的边,再从i向i-1连一条边权为0的边
大功告成!
然后我们想想好像也没啥其他条件了
跑一边spfa最短路
然后差分做出来的是相对的大小qwq
所以如果最后的dis[n]能减去一个mindis的话,解会更优且不会违背条件
然后贴代码
#include <cstdio> #include <algorithm> #include <queue> #include <cstring> using namespace std; const int MAXN = 30101; const int MAXM = 100101; int cnt=0,u[MAXM],v[MAXM],w[MAXM],first[MAXN],next[MAXM]; bool vis[MAXN]; int inq[MAXN],dis[MAXN]; int n,h; void addedge(int ux,int vx,int wx){ ++cnt; u[cnt]=ux; v[cnt]=vx; w[cnt]=wx; next[cnt]=first[ux]; first[ux]=cnt; } int spfa(int s,int t){ queue<int> q; for(int i=0;i<=n+1;i++){ dis[i]=0x3f3f3f3f; } q.push(s); dis[s]=0; inq[s]=1; vis[s]=1; while(!q.empty()){ int u=q.front(); q.pop(); vis[u]=0; for(int i=first[u];i;i=next[i]){ if(w[i]+dis[u]<dis[v[i]]){ dis[v[i]]=w[i]+dis[u]; if(!vis[v[i]]){ vis[v[i]]=1; inq[v[i]]++; q.push(v[i]); if(inq[v[i]]>n) return 0x3f3f3f3f; } } } } } int main(){ scanf("%d %d",&n,&h); int b,e,t; for(int i=1;i<=h;i++){ scanf("%d %d %d",&b,&e,&t); addedge(e,b-1,-t); } addedge(n+1,0,0); for(int i=1;i<=n;i++){ addedge(n+1,i,0); addedge(i,i-1,0); addedge(i-1,i,1); } spfa(n+1,0); int mind=0x3f3f3f3f; for(int i=0;i<=n;i++){ mind=min(mind,dis[i]); } printf("%d",dis[n]-mind); return 0; }