题解——洛谷P1250 种树(差分约束)

一道看一眼就知道差分约束的题目

但是最短路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} \le t \),方法非常简单,只需要乘上-1 就可以愉快地变成\( a_{j}-a_{i} \ge -t \)了

然后就是建立图。

瞪眼法可知我们可以使用类似前缀和的结构来维护一个到第i个位置一共种了多少颗树的信息

然后我们设\( d_{i} \) 表示到第i个位置现在有多少颗树

题目中要求的条件

BE中间种的树的数量不少于T

就可以转化为 \( d_{e}-d_{b-1} \le t \)了,然后用到刚才的转化方法

把式子化成 \( d_{b-1}-d_{e} \ge -t \)了

所以我们可以从e向b-1连一条边权为t的边

建完了这些边之后,我们会发现一个问题,这个图没办法直接跑最短路解出解废话因为这个图根本不联通啊

所以我们要来思考一下还有什么其他的约束条件

首先显而易见的有

\( d_{n+1}-d_{i} \ge 0 \ ,0 \ge i \le n \) 即 \( d_{i}-d_{n+1} \le 0 \ ,0 \ge i \le n \) ,所以我们可以从n+1的节点连一条边权为0的边到第i个节点

其次还有几个显而易见的结论

\(d_{i}-d_{i-1} \le 1 \)

\(d_{i-1} - d_{i} \le 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;
}

猜你喜欢

转载自www.cnblogs.com/dreagonm/p/9427259.html
今日推荐