【题目】
题目描述:
在 SD 食堂,所有的座位是一行一行的排列。现在有 N 个座位排成一行,依次编号 1,2,…,N,每个座位只能坐一个人,现在L想数一下有多少个人坐着,一个一个数太慢了,L 决定只选择 M 段连续的座位,对每段分别数出人数。由于食堂噪音十分嘈杂, L 无法专心,可能有数错了。但是 L 认为没有数漏,最多是重复计数导致的。现在他把得到的数据给你,希望你帮他算出,一共最多有多少人。
输入格式:
第一行 2 个整数 N,M,分别是座位数量,和 L 分成 M 段
接下来M行,每行3个整数,l,r,k,表示 L 数了 l 到 r 这一段的座位,他数出的人数是 k
输出格式:
数出一行一个整数表示答案
样例数据:
输入
4 2
1 2 1
2 4 2输出
3
备注:
数据规模与约定:
20% 数据,N,M ≤ 20
40% 数据,N,M ≤ 500
20% 数据,L选的每段座位不相交
100% 数据,1 ≤ N,M,K ≤ 100000,1 ≤ l ≤ r ≤ N
【分析】
又是一道我连20分都没有的题
dzy大佬说这是一道裸的差分约束的题,然后。。。
好吧还是差分约束不熟悉,只打过模板,没怎么练题
emmm话不多说我们来讲一下正解
有一个坑点就是不止 L 数的区间内有人,不在任意一个区间内的位置依旧可以坐人,为了答案最大这些地方应坐满人
由于是区间操作,我们用前缀和,即用 sum[ i ] 表示 i 号位置及前面的人的数量
首先对于所有的 i,都有 sum[ i ] - sum[ i - 1 ] ≥ 0,转换成 sum[ i - 1 ] - sum[ i ] ≤ 0
还有就是 sum[ i ] - sum[ i - 1 ] ≤ 1,就是每个位置最多坐一个人
然后约束就是,sum[ r ] - sum[ l ] ≤ k
按照这样建边然后跑SPFA就可以了
【代码】
为了代码简洁我把读优删了,不加读优好像是要 T 的
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2000005
#define oo 2e+9
using namespace std;
int n,m,t;
int v[N],w[N],next[N];
int d[N],q[N],first[N],sum[N];
bool vis[N];
void add(int x,int y,int z)
{
t++;
next[t]=first[x];
first[x]=t;
v[t]=y;
w[t]=z;
}
void spfa(int s)
{
int x,y,i,j,end=1;
for(i=1;i<=n;++i)
{
d[i]=oo;
vis[i]=false;
}
q[1]=s;
d[s]=0;
vis[s]=true;
for(j=1;j<=end;++j)
{
x=q[j];
vis[x]=false;
for(i=first[x];i;i=next[i])
{
y=v[i];
if(d[y]>d[x]+w[i])
{
d[y]=d[x]+w[i];
if(!vis[y])
{
q[++end]=y;
vis[y]=true;
}
}
}
}
}
int main()
{
// freopen("dining.in","r",stdin);
// freopen("dining.out","w",stdout);
int l,r,i,k;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
{
add(i,i-1,0);
add(i-1,i,1);
}
for(i=1;i<=m;++i)
{
scanf("%d%d%d",&l,&r,&k);
add(l-1,r,k);
}
spfa(0);
printf("%d",d[n]);
// fclose(stdin);
// fclose(stdout);
return 0;
}