跑操 题解报告
跑操 解题报告
简要题意
在 \([1,n\)] 上有 \(m\) 个区间,每个区间有一定价值,每个点有一定花费。对于一个由若干区间构成的集合,其的价值为其包涵区间的总价值减去所有区间的并所覆盖的所有节点的花费。我们可以从这 \(m\) 个区间中取出一个集合,求可以获得的最大价值。
数据规模:\(n,m \le 2 \times 10^5\)
分析
一眼dp,想状态设计。题目只有两个元素,一个是点,一个是区间。
先考虑区间按区间设计行不行,若设 \(f_i\) 表示考虑前 \(i\) 个区间可以获得的最大价值,我们有:
\[f_i=min(f_j+ \text{被i覆盖且未被之前覆盖过的节点的权值之和})
\]
那么发现之前取了哪些区间对我们转移时是有影响的。这需要状压,但显然不现实。
因此考虑设 \(f_i\) 表示考虑前 \(i\) 个点可以获得的最大价值。那么我们很容易列出如下方程:
\[f_i= \min_{j\in[1,i)}{f_{j-1}+cost_{i,j}+val_{i,j}}
\]
其中 \(cost_{i,j}\) 表示第 \(i\) 个点到第 \(j\) 个点的权值和,\(val_{i,j}\) 表示被区间 \([i,j]\) 完全包含的区间的价值和。
线段树维护即可。
总结
- 不好设计方程时可以考虑优化状态设计
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define Inf (1ll<<60)
#define For(i,s,t) for(int i=s;i<=t;i++)
#define Down(i,s,t) for(int i=s;i>=t;i--)
#define ls (i<<1)
#define rs (i<<1|1)
using namespace std;
typedef long long ll;
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline ll min(ll x,ll y){return x<y?x:y;}
inline ll max(ll x,ll y){return x>y?x:y;}
inline int read(){
register int x=0,f=1;
char c=getchar();
while(c<'0' || '9'<c) f=(c=='-')?-1:1,c=getchar();
while('0'<=c && c<='9') x=(x<<1)+(x<<3)+c-'0',c=getchar();
return x*f;
}
void write(int x){
if(x>=10) write(x/10);
putchar(x%10+'0');
}
const int N=2e5+100,M=N*100;
int n,m,w[N];
ll d[N],f[N],ans;
struct Line{int l,r,val;}a[N];
bool cmpr(Line x,Line y){return x.r<y.r || (x.r==y.r && x.l<x.l);}
bool cmpl(Line x,Line y){return x.l<y.l || (x.l==y.l && x.r<x.r);}
struct Tree{int l,r;ll val,tag;}t[N<<2];
void push_up(int i){t[i].val=max(t[ls].val,t[rs].val);}
void push_down(int i){
if(t[i].tag){
t[ls].val+=t[i].tag;
t[rs].val+=t[i].tag;
t[ls].tag+=t[i].tag;
t[rs].tag+=t[i].tag;
t[i].tag=0;
}
}
void build(int i,int l,int r){
t[i].l=l,t[i].r=r;
if(l==r)
return;
int mid=l+r>>1;
build(ls,l,mid);
build(rs,mid+1,r);
push_up(i);
}
void update(int i,int l,int r,ll k){
if(l<=t[i].l && t[i].r<=r){
t[i].val+=k;
t[i].tag+=k;
//printf("[%d,%d] %lld\n",t[i].l,t[i].r,t[i].val);
return;
}
push_down(i);
int mid=t[i].l+t[i].r>>1;
if(l<=mid) update(ls,l,r,k);
if(mid<r) update(rs,l,r,k);
push_up(i);
}
ll query(int i,int l,int r){
if(l<=t[i].l && t[i].r<=r)
return t[i].val;
push_down(i);
int mid=t[i].l+t[i].r>>1;
if(l>mid) return query(rs,l,r);
if(mid>=r) return query(ls,l,r);
return max(query(ls,l,r),query(rs,l,r));
}
int main()
{
freopen("runner.in","r",stdin);
freopen("runner.out","w",stdout);
n=read(),m=read();
For(i,1,n) w[i]=read();
For(i,1,m) a[i].l=read(),a[i].r=read(),a[i].val=read();
sort(a+1,a+m+1,cmpr);
int id=1;
build(1,1,n);
//For(i,1,m)
// printf("%d ",w[i]);
//putchar('\n');
For(i,1,n){
//printf("i=%d\n",i);
update(1,1,i,-w[i]);
//putchar('\n');
while(id<=m && a[id].r==i) update(1,1,a[id].l,a[id].val),id++;
//printf("\n%d\n",id);
f[i]=max(f[i-1],query(1,1,i));
if(i!=n) update(1,i+1,i+1,f[i]);
ans=max(ans,f[i]);
}
//For(i,1,n)
// printf("%lld\n",f[i]);
printf("%lld",ans);
return 0;
}