题目传送门
题意: 你在一个长度为n 的大街上,有m次烟花表演,分别在t[i]时刻上演,假设你现在站在位置x上,你观看第i次烟花表演,可以获得b[i]-abs(a[i]-x) 的幸福值(不能不看!),然后你每一个时刻可以移动d个单位长度,开始你可以在任意位置,不能走出大街,问你最多能获得多少幸福值?(可能是负数)。
数据范围:
1 ≤ n ≤ 150000 ; 1 ≤ m ≤ 300 ; 1 ≤ d ≤ n 1 ≤ n ≤ 150000; 1 ≤ m ≤ 300; 1 ≤ d ≤ n 1 ≤ n ≤ 150000;1 ≤ m ≤ 300;1 ≤ d ≤ n
1 ≤ a i ≤ n ; 1 ≤ b i ≤ 1 0 9 ; 1 ≤ t i ≤ 1 0 9 1 ≤ a_i ≤ n; 1 ≤ b_i ≤ 10^9; 1 ≤ t_i ≤ 10^9 1 ≤ ai ≤ n;1 ≤ bi ≤ 109;1 ≤ ti ≤ 109
保证t[i]由小到大给出(好像没什么用的条件,sort一下就行)
题解: 看到这个数据范围,我们肯定能想到用 f [ i ] [ j ] f[i][j] f[i][j] 表示你正站在位置j在看第i场烟花所能获得的最大值,那么最后我们的答案就是 m a x ( f [ m ] [ j ] ) , j ∈ ( 1 , n ) max(f[m][j]),j∈(1,n) max(f[m][j]),j∈(1,n)
然而,我们可以知道,第i次看烟花只会受第i-1次的影响,所以我们可以优化掉一维的空间,即得到式子 f [ j ] = m a x ( f [ k ] ) + b [ i ] − a b s ( a [ i ] − j ) , ( k ∈ ( m a x ( 1 , j − ( t [ i ] − t [ i − 1 ] ) ∗ d ) , m i n ( n , j + ( t [ i ] − t [ i − 1 ] ) ∗ d ) ) ) f[j]=max(f[k])+b[i]-abs(a[i]-j),(k∈(max(1,j-(t[i]-t[i-1])*d),min(n,j+(t[i]-t[i-1])*d))) f[j]=max(f[k])+b[i]−abs(a[i]−j),(k∈(max(1,j−(t[i]−t[i−1])∗d),min(n,j+(t[i]−t[i−1])∗d)))
这不是维护一个RMQ吗,每次询问区间k中的最大值
如果我们用线段树去维护RMQ,这题的复杂度为 O ( n m l o g ( n ) ) O(nmlog(n)) O(nmlog(n)),遗憾超时
但是我们发现我们在遍历街道的时候,之前用过的并且以后不可能再用的信息可以舍去,这就可以用单调队列去维护我们的区间最值了(滑动窗口)。
代码:
#include<bits/stdc++.h>
#define endl '\n'
#define null NULL
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define ll long long
//#define int long long
#define pii pair<int,int>
#define ull unsigned long long
#define pdd pair<double,double>
#define lowbit(x) x&-x
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
char *fs,*ft,buf[1<<20];
#define gc() (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin),fs==ft))?0:*fs++;
inline int read()
{
int x=0,f=1;
char ch=gc();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=gc();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=gc();
}
return x*f;
}
using namespace std;
const int N=150000+1500;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const double eps=1e-6;
const double PI=acos(-1);
int a[N],b[N],c[N];
ll f[2][N],q[N];
signed main()
{
int n=read(),m=read(),d=read();
for(int i=1; i<=m; i++)
{
a[i]=read();
b[i]=read();
c[i]=read();
}
int fg=0,t=c[1];
for(int i=1; i<=m; i++)
{
int l=0,r=0,k=1;
if(i==1)
{
for(int j=1; j<=n; j++)
f[fg][j]=b[i]-abs(a[i]-j);//刚开始,直接给值
}
else
{
for(int j=1; j<=n; j++)
{
while(k<=n&&k<=j+1LL*(c[i]-c[i-1])*d)//k在合法范围内
{
while(l<r&&f[fg^1][k]>=f[fg^1][q[r-1]])
//后面到的比前面的更大,就一定更优,直接插队就行了
r--;
q[r++]=k++;
}
while(l<r&&j-1LL*(c[i]-c[i-1])*d>q[l])
//已经超出范围的无用信息就直接删去,最后队列首部就是当前最优的状态
l++;
f[fg][j]=f[fg^1][q[l]]+b[i]-abs(a[i]-j);
}
}
fg^=1;
}
ll res=-1e18;
for(int i=1;i<=n;i++)
res=max(res,f[fg^1][i]);
cout<<res<<endl;
return 0;
}