[POJ1021] Intervals

传送门 >Here<

题意:给出N段区间,并告诉你每段区间里有几个数(一个位置只能放一个数) 问总共至少有几个数

解题思路

  差分约束题,本蒟蒻也是第一次做差分约束题……

  所谓差分约束,常常是通过最短路(或最长路)来解决一些约束问题,例如不等式组

  举个例子:$$x1 -x2 \leq a1 (1)$$$$x2 -x3 \leq a2 (2)$$$$x1 -x3 \leq a3 (3)$$求解$x1-x3$的解集

  则我们可以让1~2连一条长度为a1的有向边,2~3连一条长度为a2的有向边,1~3连一条长度为a3的有向边

  我们会发现$(1)+(2)$可以得到$x1 - x3 \leq a1 + a2$(利用了不等式之间相加的定理),因此得到一组不等式组$$x1 - x3 \leq a1 + a2$$$$x1 -x3 \leq a3 (3)$$

  而最终由于是小于等于的约数,解集应当取$Min\{ a1+a2, a3 \}$

  回到刚才的最短路问题上来,这就等效于a1到a3的最短路——因为每走一步就相当于不等式相加,抵消了一部分,最终得到路径两端的元素作差(中介点一定会被减掉)

  以上是差分约束的基本概念。那么回到这道题来,好像和差分约束没什么关系?

  光看好像是没什么关系。由于是考虑区间内数的个数,不妨设想有一个前缀和数组s,这样$[a_i, b_i]$至少有$c_i$个元素就可以表示成$s[b_i] - s[a_i-1] >= c[i]$,我们会得到若干个这样的不等式,就可以做差分约束了——然而值得注意的是,刚才的例子里是小于等于,而这里是大于等于。所以这里的解集应当取到最大,所以求的是最长路而不是最短路

  真的仅仅只是这样吗?我们忽略了题目给的一个条件——每个位置只能放一个数,所以我们的约束条件少了,要加上对于每一个位置i,$s[i]-s[i-1] \geq 0, s[i]-s[i-1] \leq 1$(其实$s[i]-s[i-1]$)就是i这个位置的数的个数,不是0就是1. 对于小于等于的情况,同时乘以-1转换成大于等于的形式,$s[i-1]-s[i] \geq -1$即可

code

  注意Dijkstra是不能做负权的(有$s[i-1]-s[i] \geq -1$的存在),所以用SPFA

/*By QiXingzhi*/
#include <cstdio>
#include <cstring>
#include <queue> 
#include <algorithm>
#define  r  read()
#define  Max(a,b)  (((a)>(b)) ? (a) : (b))
#define  Min(a,b)  (((a)<(b)) ? (a) : (b))
using namespace std;
typedef long long ll;
const int MAXN = 500010;
const int INF = 1061109567;
inline int read(){
    int x = 0; int w = 1; register int c = getchar();
    while(c ^ '-' && (c < '0' || c > '9')) c = getchar();
    if(c == '-') w = -1, c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar();
    return x * w;
}
int N,L,R;
int a,b,c,d[MAXN],vis[MAXN];
int first[MAXN*2],nxt[MAXN*2],to[MAXN*2],cost[MAXN*2],num_edge;
queue <int> q;
inline void add(int u, int v, int w){
//    printf("%d->%d  (%d)\n", u,v,w);
    to[++num_edge] = v;
    cost[num_edge] = w;
    nxt[num_edge] = first[u];
    first[u] = num_edge;
}
inline void SPFA(int s){
    for(int i = 0; i <= N; ++i) d[i] = -INF;
    d[s] = 0;
    q.push(s);
    int u, v;
    while(!q.empty()){
        u = q.front();
        q.pop();
        vis[u] = 0;
        for(int i = first[u]; i; i = nxt[i]){
            v = to[i];
            if(d[u] + cost[i] > d[v]){
                d[v] = d[u] + cost[i];
                if(!vis[v]){
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    }
}
inline void Init(){
    memset(vis,0,sizeof(vis));
    memset(first,0,sizeof(first));
    memset(nxt,0,sizeof(nxt));
    memset(to,0,sizeof(to));
    memset(cost,0,sizeof(cost));
}
int main(){
//    freopen(".in","r",stdin);
    while(scanf("%d",&N) == 1){
        L = N+1, R = -1;
        Init();
        for(int i = 1; i <= N; ++i){
            a=r,b=r,c=r;
            add(a, b+1, c);
            L = Min(L, a);
            R = Max(R, b+1);
        }
        for(int i = 2; i <= R; ++i){
            add(i+1, i, -1);
            add(i, i+1, 0);
        }
        SPFA(L);
    //    printf("R+1 = %d  L+1 = %d\n",R+1,L+1);
        printf("%d\n", d[R]);    
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/qixingzhi/p/9396653.html