Codeforces Round #219 (Div. 2) E. Watching Fireworks is Fun

http://codeforces.com/contest/373/problem/E

E. Watching Fireworks is Fun
time limit per test
4 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

A festival will be held in a town's main street. There are n sections in the main street. The sections are numbered 1 through n from left to right. The distance between each adjacent sections is 1.

In the festival m fireworks will be launched. The i-th (1 ≤ im) launching is on time ti at section ai. If you are at section x (1 ≤ xn) at the time of i-th launching, you'll gain happiness value bi - |ai - x| (note that the happiness value might be a negative value).

You can move up to d length units in a unit time interval, but it's prohibited to go out of the main street. Also you can be in an arbitrary section at initial time moment (time equals to 1), and want to maximize the sum of happiness that can be gained from watching fireworks. Find the maximum total happiness.

Note that two or more fireworks can be launched at the same time.

Input

The first line contains three integers nmd (1 ≤ n ≤ 150000; 1 ≤ m ≤ 300; 1 ≤ dn).

Each of the next m lines contains integers aibiti (1 ≤ ain; 1 ≤ bi ≤ 109; 1 ≤ ti ≤ 109). The i-th line contains description of the i-th launching.

It is guaranteed that the condition titi + 1 (1 ≤ i < m) will be satisfied.

Output

Print a single integer — the maximum sum of happiness that you can gain from watching all the fireworks.

Please, do not write the %lld specifier to read or write 64-bit integers in C++. It is preferred to use the cincout streams or the %I64dspecifier.

Sample test(s)
input
50 3 1
49 1 1
26 1 4
6 1 10
output
-31
input
10 2 1
1 1000 4
9 1000 4
output
1992

题目分析:核心在如何减小∑|ai-x|

依然是长度不等的柱子,一时间顺序排列(ai),需要确定所有的xi,使得∑|ai-x|最小,当然两个xi之间需要可达。

这个问题确实很难,但是由于m=300,貌似暴力点是可以过的。n=150000。 【dp】

dp[i][j] 前i个柱子(ai),以xi=j为决策所能达到的最小的 ∑|ai-x|

dp复杂度是n*m=150000*300,而方程转移复杂度是O(n)

因此,需要减小方程转移的复杂度。转移集合D={dp[i-1][r] |  r 和 j 可达},转移目标是求D的最小值。

可以发现转移集合D是连续的,r是连续的,因此问题变成了 求解区间最小值。

思路有二:

第一,线段树,每次将第i-1的数据放入一颗线段树,对于计算i 的时候,查询是log(n)【当然,此题修改和查询较简单,树状数组也可以实现最小值查询】

第二,单调队列,转移集合D只需要在j=1时求一次,以后j增加的时候,集合D最多只修改了两个元素。而建立一个递增的单调队列,维护待查询 区间的最小值。


思路1的复杂度较大,m*n*log(n),会T。但是叫一个小优化就过了。某些时候,最小值具有连续性,因此并不是每一次都需要去线段树里面查询。

思路2的解法比较常规,复杂度m*n。


下面是AC的代码,基于思路1+树状数组最小值查询+小优化。


#include<algorithm>
#include<iostream>
#include<fstream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<string>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<ctime>

using namespace std;
#define FOR(i,a,b)    for(int i=a;i<b;i++)
#define FORU(i,a,b)    for(Uint i=a;i<b;i++)
#define FORD(i,a,b)    for(int i=a;i>b;i--)
#define MST(a,num)    memset(a,num,sizeof(a))
#define MCP(d,s)    memcpy(d,s,sizeof(s))
#define WH(n)         while(scanf("%d", &n) != EOF)
#define WHZ(n)         while(scanf("%d", &n) != EOF && n != 0)
#define SCF(a)        scanf("%d",&a)
#define SCFS(a)        scanf("%s",a)
#define PRF(a)        printf("%d",a)
#define PRS(a)        printf("%s",a)
#define PRFF(a)        printf("%d
",a)
#define PRSF(a)        printf("%s
",a)
#define PRFFU(a)        printf("%I64d
",a)

#define PI acos(-1)
#define min2(a,b)     (a<b)?a:b
#define max2(a,b)     (a>b)?a:b
#define max3(a,b,c)     max(max(a,b),c)
#define max4(a,b,c,d)     max(max(a,b),max(c,d))

#define FORE(e,x) for(__typeof(x.begin()) e=x.begin(); e!=x.end(); e++) //foreach(it, ans ) cout<<*it<<" ";
#define all(a) (a).begin(),(a).end() //sort(all(v));
#define len(a) ((int)(a).size())
#define pb push_back
#define mk make_pair
#define V(etype) vector<etype>

typedef __int64 Uint;
typedef vector<int> Vint;
typedef pair<int,int>mypair;

#define INF 0x3f3f3f3f
#define eps 1e-9
const int N=300000+100;

int val[N];
int sum[N];
struct Bitree{
int n;//1 -> n
Bitree(int k){
	n=1<<k;
	clear();
}
void clear(){
	FOR(i,1,n+1)sum[i]=val[i]=INF;
}
void add(int i,int _val){
	val[i]=_val;
	for(;i<=n;i+=-i&i)sum[i]=min(sum[i],_val);
}
bool cover(int i,int j)//whether i cover j,j<i
{
return j+(-i&i)>i;
}
int query(int j,int i){//[j,i]
int ret=INF;
for(;i>=j;){
	if(cover(i,j)){ret=min(ret,val[i]);i--;}
	else {ret=min(ret,sum[i]);i-=-i&i;}
}
return ret;
}
};
int dp[N];
int pos[N];
int b[N];
int t[N];
int main(){
int n,m,d;
Uint ret;
Bitree tree(18);
while(cin>>n>>m>>d){
FOR(i,0,m)scanf("%d%d%d",&pos[i],&b[i],&t[i]);
ret=0;
int tmp;
Uint ad;
FOR(j,1,n+1)dp[j]=abs(pos[0]-j);
FOR(i,1,m){
ad=(t[i]-t[i-1]);
ad*=d;
FORU(j,1,n+1){
if(j==1){
	tree.clear();
	FOR(r,1,n+1)tree.add(r,dp[r]);
	tmp=tree.query(max2(1,j-ad),min2(n,j+ad));
}
else 
	if(j+ad <= n && val[j+ad]<=tmp)tmp=val[j+ad]; 
	else
		if(j-ad<=1 || val[j-ad - 1]>tmp);
		else tmp=tree.query(max2(1,j-ad),min2(n,j+ad));
dp[j]=tmp+abs(pos[i]-j);
}
}
ret=INF;
FOR(j,1,n+1)ret=min2(ret,dp[j]);
ret=-ret;
FOR(i,0,m)ret+=b[i];
PRFFU(ret);
}
return 0;
}


原文地址:https://www.cnblogs.com/fuhaots2009/p/3476577.html