C++——NOIP模拟题——种树

种树

题目描述

为了绿化乡村,H 村积极响应号召,开始种树了。

H 村里有 n 幢房屋,这些屋子的排列顺序很有特点,在一条直线上。于是方便起见,我们给它们标上 1~n 。树就种在房子前面的空地上。

同时,村民们向村长提出了 m 个意见,每个意见都是按如下格式:希望第 li 个房子到第 ri 个房子的房前至少有 ci 棵树。

因为每个房屋前的空地面积有限,所以每个房屋前最多只能种 k棵树。

村长希望在满足村民全部要求的同时,种最少的树以节约资金。请你帮助村长。

输入格式

输入文件输入第 1 行,包含两个整数 n,m 。
第 2 行,有 n 个整数 ki
第 2~m+1 行,每行三个整数 li,ri,c

输出格式

输出 1 个整数表示在满足村民全部要求的情况下最少要种的树。村民提的要求是可以全部满足的。

样例数据 1

输入

5 3 
1 1 1 1 1 
1 3 2 
2 4 2 
4 5 1

输出

3

样例数据 2

输入

4 3 
3 2 4 1 
1 2 4 
2 3 5 
2 4 6

输出

8

备注

【样例1解释】 
如图是满足样例的其中一种方案,最少要种 3 棵树。

【样例2解释】
如图是满足样例的其中两种方案,左图的方案需要种 9 棵树,右图的方案需要种 8 棵树。可以验证,最少需要种 8 棵树。 

【数据范围】
对于30%的数据,0i=1; 
对于50%的数据,0i≤100; 
对于70%的数据,0i≤1,000; 
对于100%的数据,0i≤5,000

解题报告:

关键词:差分约束系统
开一个s数组记录前缀和。
根据题意我们可以得到3个约束条件: 
s[r]-s[l-1]≥c,① 
s[i]≥s[i-1],② 
s[i]-s[i-1]≤k,③ 
根据①得s[l-1]-s[r]≤-c,在r和l-1之间连一条权值为-c的边。
根据②得s[i-1]-s[i]≤0,在i和i-1之间连一条权值为0的边。
根据③在i-1和i之间连一条权值为k的边。
50分算法:Bellman-Ford。时间复杂度:O(nm) 
70分算法:SPFA。时间复杂度:O(km) 
100分算法:观察到最大可能需要连150w条边,因此我们要考虑有些边是否需要连。
我们可以只根据条件①计算,每次更新后O(n)检查是否满足条件②和③,如果不满足就修改,这样只用连50w条边,可以过全部数据。 

#include
#include
struct node{
	long long u,v,w;
}e[500001];
long long d[500001],k[500000],n,m;
inline long long readint()
{
    long long i=0,f=1;
    char ch;
    for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
    for(;ch>='0' && ch<='9';ch=getchar())
        i=(i<<3)+(i<<1)+ch-'0';
    return i*f;
}
int main()
{
	freopen("tree.in","r",stdin);
	n=readint();
	m=readint();
	register long long i,f=1,t;
	for(i=1;i<=n;++i) k[i-1]=readint();
	for(i=1;i<=m;++i)
	{
		e[i].v=readint()-1;
		e[i].u=readint();
		e[i].w=-readint();
	}
	while(f)
	{
		f=0;
		for(i=1;i<=m;++i)
			if(d[e[i].v]>(t=d[e[i].u]+e[i].w)) d[e[i].v]=t,f=1;
		for(i=1;i<=n;++i)
			if(d[i]-d[i-1]>k[i-1]) d[i]=d[i-1]+k[i-1],f=1;
		for(i=n;i;--i)
			if(d[i-1]>d[i]) d[i-1]=d[i],f=1;
	}
	printf("%d\n",d[n]-d[0]);
	return 0;
}

var
u,v,w,d,k:array[0..500000]of longint;
n,m,i,f,t,l,r,c,ans:longint;
begin
        readln(n,m);
        for i:=1 to n do read(k[i-1]);
        for i:=1 to m do begin
        readln(l,r,c);
        v[i]:=l-1;
        u[i]:=r;
        w[i]:=-c;
        end;
        f:=1;
        while f > 0 do begin
        f:=0;
        for i:=1 to m do begin
        t:=d[u[i]]+w[i];
        if d[v[i]]>t then begin
        d[v[i]]:=t;
        f:=1;
        end;
        end;
        for i:=1 to n do begin
        if d[i]-d[i-1]>k[i-1] then begin
        d[i]:=d[i-1]+k[i-1];
        f:=1;
        end;
        end;
        for i:=n downto 1 do begin
        if d[i-1]>d[i] then begin
        d[i-1]:=d[i];
        f:=1;
        end;
        end;
        end;
        ans:=d[n]-d[0];
        writeln(ans);
end.  


你可能感兴趣的:(C++,Pascal)