给定 n 个区间 [ai,bi]和 n 个整数 ci。
你需要构造一个整数集合 Z,使得∀i∈[1,n],Z 中满足ai≤x≤bi的整数 x 不少于 ci 个。
求这样的整数集合 Z 最少包含多少个数。
输入格式
第一行包含整数 n。
接下来n行,每行包含三个整数ai,bi,ci。
输出格式
输出一个整数表示结果。
数据范围
1≤n≤50000,
0≤ai,bi≤50000,
1≤ci≤bi−ai+1
输入样例:
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
输出样例:
6
思路:
定义
s
k
sk
sk为0~k整数中选择了多少个数字。那么题意就转换为
s
[
b
i
]
−
s
[
a
i
−
1
]
≥
c
i
s[bi] - s[ai - 1]≥ci
s[bi]−s[ai−1]≥ci
这是一个明显的差分约束式子求的是最长路。
但是这是一个数列,里面有一些隐性的条件。
比如对于数据 1 2 3, 4 5 3
对于
s
[
2
]
=
3
,
s
[
0
]
=
0
,
s
[
5
]
=
3
,
s
[
3
]
=
0
s[2]=3,s[0]=0,s[5]=3,s[3]=0
s[2]=3,s[0]=0,s[5]=3,s[3]=0
这是符合差分式的,但是明显不合理:
s
[
3
]
s[3]
s[3]不应该小于
s
[
2
]
s[2]
s[2]。
隐性的不等式是: s [ i ] − s [ i − 1 ] ≥ 0 s[i] - s[i - 1] ≥ 0 s[i]−s[i−1]≥0 s [ i ] − s [ i − 1 ] ≤ 1 s[i] - s[i-1]≤1 s[i]−s[i−1]≤1(这是一个整数集合)
因为 c [ i ] ≤ b [ i ] − a [ i ] + 1 ) c[i] ≤ b[i] - a[i] + 1) c[i]≤b[i]−a[i]+1),所以不可能出现正环(从b[i]走回到a[i] - 1要经过b[i] - a[i] + 1个-1)
再说说超级原点的问题。
本题中可以直接以-1为起点(然后再把所有点往右平移)。
因为很明显可以确定s[-1] = 0(也就是-1点与其他点的关系),且可以从-1出发松弛其他所有点。
有的差分约束系统中就必须得确定一个超级源点连接其他所有点,比如0,0连接其他所有点,边权为0,并设置d[0] = 0(如果是 d [ x ] + w ≤ d [ y ] d[x]+w≤d[y] d[x]+w≤d[y]的差分约束,那么求出来的其他值都是0或负数)。
设置超级源点,个人以为其实就是变相的多源最短(长)路,因为你不清楚要以哪一个点为起点,如果设置了一个点为起点,但是这个点出发不能到达其他点,那就起不到松弛全图的作用了。
比如 1 -> 2 -> 3 -> 4,你不能设置4起点哈,不然的话你怎么确定1,2,3的值。
所以超级源点就是多源点的一种表示形式。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 1e5 + 7;
int head[maxn],to[maxn],nex[maxn],val[maxn],tot;
int v[maxn],d[maxn];
void add(int x,int y,int z) {
to[++tot] = y;
nex[tot] = head[x];
val[tot] = z;
head[x] = tot;
}
void spfa() {
queue<int>q;
memset(d,0xcf,sizeof(d));
q.push(0);d[0] = 0;v[0] = 1;
while(!q.empty()) {
int x = q.front();q.pop();
v[x] = 0;
for(int i = head[x];i;i = nex[i]) {
int y = to[i],w = val[i];
if(d[y] < d[x] + w) {
d[y] = d[x] + w;
if(!v[y]) {
v[y] = 1;
q.push(y);
}
}
}
}
}
int main() {
int n,m = 0;scanf("%d",&n);
for(int i = 1;i <= n;i++) {
int x,y,z;scanf("%d%d%d",&x,&y,&z);
add(x,y + 1,z);
m = max(m,y + 1);
}
for(int i = 1;i <= m;i++) {
add(i - 1,i,0);
add(i,i - 1,-1);
}
spfa();
printf("%d\n",d[m]);
}