P6881 [JOI 2020 Final] 火事 題解
阿新 • • 發佈:2021-09-05
Link.
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; }