1. 程式人生 > >P1250種樹(差分約束)

P1250種樹(差分約束)

P1250種樹

題意

給你一條街,街由N個點構成,每個點上可以種一棵樹,給你K個要求,每個要求由B,E,T組成,分別表示從點B開始到點E中間至少有T棵樹,現在問這條街上最少有幾顆樹?

資料範圍:0<N<=3104,0<K<=5000,0<B<=E<=300000<N<=3*10^4,0<K<=5000,0<B<=E<=30000

思路

設 sum[i]為到點i為止的字首和。

將題目的意思稍加轉換可得,求最小數量的sum[N],來使得所有要求滿足 sum[E] - sum[B-1] >= T

這樣就是求差分約束的問題了

我們可以利用最短路的計算公式, dis[u] + cost(u,v) >= dis[v]

來將其構造成一個最短路問題。即轉換成 sum[E] + (-T) >= sum[B-1]

這樣還不夠,題目中還隱藏了一些資訊,就是 每個點最多隻能種一棵,每兩個相鄰的點,後面的點值必定大於前面的點值。

最後跑一邊SPFA即可,但使用Djs好像會T。。。。。

程式碼

// luogu-judger-enable-o2
#include <bits/stdc++.h>
using namespace std; #define rep(i,j,k) for (int i = (int)j;i <= (int)k;i ++) #define debug(x) cerr<<#x<<":"<<x<<endl #define pb push_back typedef long long ll; const int MAXN = (int)1e6+7; const int INF = (int)0x3f3f3f3f; struct edge{ int v,cost; edge(int v = 0,int
cost = 0):v(v),cost(cost){} }; vector<edge> G[MAXN]; int dis[MAXN]; void SPFA(int S) { dis[S] = 0; queue<int> qu; qu.push(S); while (!qu.empty()) { int k = qu.front();qu.pop(); rep(i,0,G[k].size()-1) { int v = G[k][i].v; int co = G[k][i].cost; if (dis[v] > dis[k] + co) { dis[v] = dis[k] + co; qu.push(v); } } } } int main() { int N,M; scanf("%d %d",&N,&M); memset(dis,0x3f,sizeof(int)*(N+2)); rep(i,1,M) { int u,v,w; scanf("%d %d %d",&u,&v,&w); G[v].pb(edge(u-1,-w)); } rep(i,1,N) { G[i-1].pb(edge(i,1)); G[i].pb(edge(i-1,0)); } SPFA(N); int mn = INF; rep(i,0,N) { mn = min(mn,dis[i]); } int ans = dis[N]-mn; printf("%d\n",ans); }