题目大意
给定n个区间
[Li,Ri]
,以及n个整数
vi
.
现在要有一个集合,使得这个集合和任意
[Li,Ri]
都有
vi
个元素相同.
问这个集合最少要几个元素.
题解
这题乍一看和差分约束系统没有什么关系.
但是转化一下,就会发现其中的精妙了.
定义
S(x)
表示
[1,x]
中选择的元素的个数.
那么就有
S(Ri)−S(Li−1)>=vi
然而,这是不够的,因为图可能不连通.
这时候就要发掘题目的隐含条件了.
可以发现
S(i)−S(i−1)>=0
,因为i要么取要么不取,但不管怎么样,
S(i)
都大于
S(i−1)
并且
S(i−1)−S(i)>=−1
,因为
S(i)−S(i−1)<=1
.
这样就可以构图了.
然后再考虑怎么求解.
在图中,一条边[a,b,c],表示
S(a)−S(b)>=c
.
考虑a->b,如果有两条路径a->c->b,a->d->b,由于要满足所有条件,所以
dis[a][b]=max(dis[a][d]+dis[d][b],dis[a][c]+dis[c][b])
所以答案就是求最长路.
实现时,为了方便,所以把下标都加一了.
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int M=50005;
struct Edge{
int to,v,nxt;
Edge(int _to=0,int _v=0,int _nxt=0):to(_to),v(_v),nxt(_nxt){}
}edge[M<<2];
int etot,head[M],mark[M],Q[M*40],dis[M];
void SPFA(int st){
mark[st]=1;
dis[st]=0;
int L=0,R=0;
Q[R++]=st;
while(L<R){
int x=Q[L++];
mark[x]=0;
for(int i=head[x];~i;i=edge[i].nxt){
int to=edge[i].to;
if(dis[to]<dis[x]+edge[i].v){
dis[to]=dis[x]+edge[i].v;
if(!mark[to]){
Q[R++]=to;
mark[to]=1;
}
}
}
}
}
void add_edge(int a,int b,int c){
edge[etot]=Edge(b,c,head[a]);
head[a]=etot++;
}
int main(){
int n;
while(scanf("%d",&n)!=EOF){
int mx=0,mi=M;
etot=0;
memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++){
int L,R,v;
scanf("%d%d%d",&L,&R,&v);
add_edge(L,R+1,v);
mx=max(mx,R+1);
mi=min(mi,L);
}
for(int i=mi+1;i<=mx;i++){
add_edge(i-1,i,0);
add_edge(i,i-1,-1);
}
for(int i=mi;i<=mx;i++)
mark[i]=0,dis[i]=-M;
SPFA(mi);
printf("%d\n",dis[mx]);
}
return 0;
}