1. 程式人生 > 其它 >P6881 [JOI 2020 Final] 火事 題解

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;
}