Loading

【题解】[JOI 2020 Final] 火事

这题看起来人畜无害,非常小清新的 DS 题,但写起来就不会这么想了。

题目大意,给定一个长度为 \(N\) 的序列 \(S\) ,需要回答 \(Q\) 次询问,每次询问 \(\sum\limits_{L\le i\le R}\max\limits_{i-T\le j\le i}\{S_j\}\)

子任务 \(1\) ,直接模拟,时间复杂度 \(\mathcal{O}(QN^2)\)

子任务 \(2\)\(T_i\) 相等,直接算出 \(T\) 时刻的序列,然后求一个前缀和即可。计算序列时可以单调队列做到 \(\mathcal{O}(N)\)

子任务 \(3\)\(L_i=R_i\) ,单点询问,直接计算 \(\max\limits_{L-T\le i\le L}\{S_i\}\) ,ST 表可以做到 \(\mathcal{O}(N\log N+Q)\)

子任务 \(4\)\(1\le S_i\le 2\)

我们不妨列出一个表格,\(S_{i,j}\) 表示时刻 \(i\) ,序列第 \(j\) 位的是 \(1\) 还是 \(2\)

先将整个表格用 \(1\) 填充,然后发现如果第 \(i\) 位为 \(2\) ,那么表格中位于直线 \(y=x-i\) 斜上方(包括)的且位置 \(\ge i\) 的所有格子都被填上 \(2\)

这就是扫描线模板,差分一下可以做到 \(\mathcal{O}(N+Q\log N)\)

对于满分做法,我们考虑延续前面的思路。

对于每一个位置 \(i\) 上的数,如果前面有比它大的数,那么它在表格中填充的部分一定是平行四边形,否则一定是一个直角梯形。

对于一个平行四边形,我们拆成三个在 \(x\) 轴上的等腰直角三角形。

现在只用考虑三角形的情况。

我们可以用一条斜率为 \(1\) 的直线和 \(x\) 轴切出一个等腰直角三角形的一个角。

对于这个角的对边,我们再拆成一个等腰直角三角形和一个长方形。

长方形已经可以直接做了,这两个等腰直角三角形都具有可以向右无限扩展的特点,我们可以用两条直线表示,在形如 \(y=x+b\) 的直线协下方,在形如 \(y=a\) 的直线上方。

转化为差分,我们需要支持区间加和区间求和。可以树状数组再做一次差分,也可以直接线段树。

由于笔者写法比较丑需要负数下标,所以使用了线段树。时间复杂度 \(\mathcal{O}((N+Q)\log N)\)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 500005
#define int long long
using namespace std;
int n,m,s[N],sta[N],pre[N],nxt[N],ans[N],top;
typedef pair<int,int> Pr;
vector<Pr>c[N],d[N];
struct seg{
	struct node{
		int l,r,tag,sum;
	}a[N<<2];
	#define L a[x].l
	#define R a[x].r
	#define ls (x<<1)
	#define rs (ls|1)
	#define T a[x].tag
	#define S a[x].sum
	inline int g(int l,int r){return l+(r-l)/2;}
	void build(int x,int l,int r){
		L=l;R=r;S=T=0;
		if(l==r)return ;
		int mid=g(l,r);
		build(ls,l,mid);
		build(rs,mid+1,r);
	}
	void pushup(int x,int val){T+=val;S+=(R-L+1)*val;}
	void down(int x){if(T)pushup(ls,T),pushup(rs,T),T=0;}
	void change(int x,int l,int r,int val){
		if(L>=l&&R<=r)pushup(x,val);
		else{
			down(x);int mid=g(L,R);
			if(mid>=l)change(ls,l,r,val);
			if(mid<r)change(rs,l,r,val);
			S=a[ls].sum+a[rs].sum;
		}
	}
	int ask(int x,int l,int r){
		if(L>=l&&R<=r)return S;
		down(x);int sum=0,mid=g(L,R);
		if(mid>=l)sum+=ask(ls,l,r);
		if(mid<r)sum+=ask(rs,l,r);
		return sum;
	}
}p,q;
void tri(int l,int r,int val){
	//cout<<"cc "<<l<<" "<<r<<" "<<val<<endl;
	if(l>r)return;
	c[0].push_back(make_pair(l,val));
	c[r-l+1].push_back(make_pair(l,-val));
	d[0].push_back(make_pair(r+1,-val));
	d[r-l+1].push_back(make_pair(r+1,val));
}
void par(int l,int r,int len,int val){
	int st=l-len+1;
	tri(st,r,val);tri(st,l-1,-val);tri(l+1,r,-val);
}
struct node{int t,l,r,op;bool operator<(const node o)const{return t<o.t;}}u[N];
signed main(){
	scanf("%lld%lld",&n,&m);int ll=-n-5,rr=n+5;
	rep(i,1,n)scanf("%lld",&s[i]);
	rep(i,1,n){
		while(top&&s[sta[top]]<=s[i])nxt[sta[top]]=i,top--;
		if(top)pre[i]=sta[top];else pre[i]=-n-2;
		sta[++top]=i;
	}
	rep(i,1,n)if(!nxt[i])nxt[i]=n+1;
	//rep(i,1,n)printf("ss %lld %lld \n",pre[i],nxt[i]);
	rep(i,1,n)par(i,nxt[i]-1,i-pre[i],s[i]);
	rep(i,1,m)scanf("%lld%lld%lld",&u[i].t,&u[i].l,&u[i].r),u[i].op=i;
	sort(u+1,u+m+1);int j=1;
	p.build(1,ll,rr);q.build(1,ll,rr);
	rep(t,0,n){
		//cout<<"st "<<t<<endl;
		for(int i=0;i<(int)c[t].size();i++){
			p.change(1,c[t][i].first,rr,c[t][i].second);
			//cout<<"Add A  "<<c[t][i].first<<" "<<c[t][i].second<<endl;
		}
		for(int i=0;i<(int)d[t].size();i++){
			q.change(1,d[t][i].first,rr,d[t][i].second);
			//cout<<"Add B  "<<d[t][i].first<<" "<<d[t][i].second<<endl; 
		}
		while(j<=m&&u[j].t==t){
			ans[u[j].op]=q.ask(1,u[j].l,u[j].r)+p.ask(1,u[j].l-t,u[j].r-t);
			j++;
		}
	}
	rep(i,1,m)printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2021-06-08 21:38  7KByte  阅读(262)  评论(0编辑  收藏  举报