P9871题解

P9871 [NOIP2023] 天天爱打卡

题目传送门

题解

T4 天天爱打卡(run)

考察:\(dp\)、线段树

先考虑 \(n=10^5\) 的部分分。动态规划是显然的:记 \(f_{i,0/1}\) 表示前 \(i\) 位,最后一位选/不选的最大能量。转移: \(f_{i,0}=\max(f_{i-1,0},f_{i-1,1})\)\(f_{i,1}=\max_{i-j+1\le k}(f_{j-1,0}+val(j,i))\)。也是显然的线段树优化,把 \(val\) 拆成加减两部分,减的部分是简单的,而加的部分对挑战按结束时间排序,然后指针扫描即可。

接下来考虑满分做法,基于一个贪心理念:跑步的目的是为了完成挑战,所以显然就是对所有挑战的起始与终止点进行离散化,但是转移就有点不同了。

\(f_{i,0/1}\) 表示前 \(i\) 个关键点,最后一个选/不选的最大能量。

\(f_{i,0}=\max(f_{i-1,0},f_{i-1,1})\) 是同理的,但 \(f_{i,1}\) 稍微有点不同,首先,我们枚举最后一段跑步之后,在上文中我们只能从 \(f_{j-1,0}\) 转移,这是为了使跑步不再连续,但在这儿,我们是可能可以从 \(f_{j-1,1}\) 转移的,因为两个关键点之间也许有空隙,我们只要在空隙中不跑步那么依然是不连续的,所以要特判关键点是否连续。然后转移的范围可以使用另一个指针来扫描,\(val\) 中加的部分与上文相同(因为所有起始终止点都是作为关键点参与离散化的),可减的部分就有点不同了:我们把其拆为 \(-d(p_i-p_{i-1})-d(p_{i-1}-p_{i-2})-\dots-d(p_{j+1}-p_j)-d\),然后考虑贡献,但是千万注意最后的那个 \(-d\)!被坑了好久,,,

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read() {
	int s=0,m=0;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-')m=1;ch=getchar();}
	while( isdigit(ch)) s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
	return m?-s:s;
}
#define inf ((int)1e12)
int c,t,n,m,k,d,b[200005],cnt,f[200005][2];
struct QWQ {int l,r,v;} a[100005];
bool cmp(QWQ a1,QWQ a2) {return a1.r<a2.r;}
struct Node {int l,r,d,laz;};
struct Segment_Tree {
  	Node t[4*200005];
	int ls(int p) {return p<<1;}
	int rs(int p) {return p<<1|1;}
	void pushup(int p) {t[p].d=max(t[ls(p)].d,t[rs(p)].d);}
	void pushdown(int p) {
		t[ls(p)].d+=t[p].laz,t[rs(p)].d+=t[p].laz;
		t[ls(p)].laz+=t[p].laz,t[rs(p)].laz+=t[p].laz;t[p].laz=0;
	}
	void clear(int p,int l,int r) {
		t[p].l=l,t[p].r=r,t[p].d=-inf,t[p].laz=0;
		if(l==r) return;int m=(l+r)>>1;
		clear(ls(p),l,m);clear(rs(p),m+1,r);
	}
	void update(int p,int l,int r,int v) {
		if(l>r) return;
		if(l<=t[p].l&&t[p].r<=r) {t[p].d+=v,t[p].laz+=v;return;}
		pushdown(p);int m=(t[p].l+t[p].r)>>1;
		if(l<=m) update(ls(p),l,r,v);
		if(r>m) update(rs(p),l,r,v);pushup(p);
	}
	int query(int p,int l,int r) {
		if(l<=t[p].l&&t[p].r<=r) return t[p].d;
		pushdown(p);int s=-inf,m=(t[p].l+t[p].r)>>1;
		if(l<=m) s=max(s,query(ls(p),l,r));
		if(r>m) s=max(s,query(rs(p),l,r));return s;
	}
} tt;
signed main() {
	cin>>c>>t;
	while(t--) {
		cin>>n>>m>>k>>d;
		b[cnt=1]=n;
		for(int i=1;i<=m;i++) {
			int x=read(),y=read();
			b[++cnt]=a[i].l=x-y+1,b[++cnt]=a[i].r=x,a[i].v=read();
		}
		sort(b+1,b+cnt+1);int q=unique(b+1,b+cnt+1)-b-1;
		tt.clear(1,1,200002);
		memset(f,-0x3f,sizeof(f));f[0][0]=0;tt.update(1,1,1,inf);
		for(int i=1;i<=m;i++)
			a[i].l=lower_bound(b+1,b+q+1,a[i].l)-b,a[i].r=lower_bound(b+1,b+q+1,a[i].r)-b;
		sort(a+1,a+m+1,cmp);
		for(int i=1,j=1,r=1;i<=q;i++) {
			tt.update(1,1,i-1,-d*(b[i]-b[i-1]));
			while(b[i]-b[j]+1>k) j++;
			while(r<=m&&a[r].r==i)
				tt.update(1,1,a[r].l,a[r].v),r++;
			f[i][0]=max(max(f[i-1][0],f[i-1][1]),0ll);
			f[i][1]=tt.query(1,j,i)-d;
			if(b[i+1]-b[i]==1) tt.update(1,i+1,i+1,inf+f[i][0]);
			else tt.update(1,i+1,i+1,inf+max(f[i][0],f[i][1]));
		}
		printf("%lld\n",max(f[q][0],f[q][1]));
	}
	return 0;
}

时间复杂度 \(O(m\log m)\)

posted @ 2024-01-19 15:33  operator-  阅读(56)  评论(0编辑  收藏  举报