P6881 [JOI 2020 Final] 火事 题解

Luogu

Description.

给定一个序列,初始为 \(\{a_i\}\)
每时刻 \(\forall i\in(1,n],a_i\leftarrow\max(a_{i-1},a_i)\)
问第 \(t\) 时刻 \(\sum_{l=1}^ra_i\)

Solution.

想了一年,要么就是建树然后暴跳,要么就是分段然后合并。
最后无一例外假了,因为没有优化到本质。
本质的优化其实是前缀和,然后平移坐标。

首先,我们考虑每一次一段极长递减序列会向右移动,并把左边一个留下。
然后每次极长连续段是可能合并的,而且合并的时间戳维护起来很困难。
考虑脑补一下图大概长什么样,连续段在向右吞噬的同时左边被蚕食。
所以应该是一些斜率为 \(1\) 的斜线和竖线组成的图形,例如样例是下图

9 3 2 6 5
9 9 3 6 6
9 9 9 6 6
9 9 9 9 6
9 9 9 9 9
.
.
.

刚开始想用 set 维护极长不增段,发现每法搞。
但是如果是斜线的话,可以考虑开另一个数据结构,支持“坐标平移”和单点修改。
首先想到一件事,就是如果从左往右扫描答案是无法更新的,只能上下扫描。
所以现在相当于有一些平行四边形,恰好覆盖了整个平面,问区间和。
平行四边形可以通过差分变成三角形,然后对于一个三角形,它由一条斜边和一条竖边组成。
然后直接开两个树状数组维护单点修改和查询前缀和的前缀和即可。
发现在坐标平移的情况下,前缀和的前缀和不变,直接平移即可。

Coding.

点击查看 /dk 代码
//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
	x=0;char c=getchar(),f=0;
	for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
	for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}//}}}
const int N=200005;int n,m,a[N],st[N],tp,ls[N],nx[N];ll rs[N];
struct segm
{
	ll T1[400015],T2[400015];
	inline void add(int x,int w)
	{
		x+=200003;int v=x;
		for(;x<=400010;x+=x&(-x)) T1[x]+=w,T2[x]+=1ll*v*w;
	}
	inline ll qry(int x)
	{
		x+=200003;ll r1=0,r2=0;int v=x;
		for(;x;x-=x&(-x)) r1+=T1[x],r2+=T2[x];
		return r1*(v+1)-r2;
	}
	inline ll qry(int l,int r) {return qry(r)-qry(l-1);}
	inline ll deb(int x) {x+=200003;ll r=0;for(;x;x-=x&(-x)) r+=T1[x];return r;}
}T1,T2;vector<pair<int,int> >v1[N<<1],v2[N<<1];//1正2斜
inline void addt(int l,int r,int v)
{
	T1.add(r+1,-v),v1[r-l+1].push_back(make_pair(r+1,v));
	T2.add(l,v),v2[r-l+1].push_back(make_pair(l,-v));
}
struct que{int l,r,id;};vector<que>q[N];
int main()
{
	read(n,m);for(int i=1;i<=n;i++) read(a[i]);
	st[tp=1]=0;for(int i=1;i<=n;i++)
	{
		while(tp&&a[st[tp]]<a[i]) tp--;
		ls[i]=st[tp],st[++tp]=i;
	}a[st[tp=1]=n+1]=1e9+5;
	for(int i=n;i>=1;i--)
	{
		while(tp&&a[st[tp]]<=a[i]) tp--;
		nx[i]=st[tp],st[++tp]=i;
	}
	for(int i=1;i<=n;i++)
	{
		int hei=ls[i]?i-ls[i]:n+1,wei=nx[i]-i;
		addt(i-hei+1,i+wei-1,a[i]),addt(i-hei+1,i-1,-a[i]),addt(i+1,i+wei-1,-a[i]);
	}
	for(int i=1,t,l,r;i<=m;i++) read(t,l,r),q[t].push_back((que){l,r,i});
	for(int t=0;t<=n;t++)
	{
		for(auto x:v1[t]) T1.add(x.first,x.second);
		for(auto x:v2[t]) T2.add(x.first,x.second);
		//for(int i=1;i<=n;i++) printf("%lld%c",T1.deb(i)+T2.deb(i-t),i==n?'\n':' ');
		for(auto x:q[t]) rs[x.id]=T1.qry(x.l,x.r)+T2.qry(x.l-t,x.r-t);
	}
	for(int i=1;i<=m;i++) printf("%lld\n",rs[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/pealfrog/p/15229253.html