【JOI 2020 Final】【LOJ3256】火災(掃描線)(樹狀數組)

傳送門


題解:

首先容易注意到詢問可以查分爲前綴詢問。

然後注意到詢問本質上就是求 [iT,i][i-T,i] 的最大值之和。

一個比較直接的思路是考慮每個值在答案中出現了多少次,發現不好維護,不過這可以給我們一些啓發。

考慮統計每個位置上值的變化量。

構造一個平面直角座標系,橫座標表示序列中的下標,縱座標表示時間,每個整點上有一個值表示 Sx(y)S_x(y)

初始化所有位置的值爲原序列中的值,考慮怎麼通過若干次修改得到最終的平面。

對每個 ii,求出它作爲最大值存在的區間 (li,ri)(l_i,r_i),容易發現我們需要考慮的就是 lil_i 什麼時候覆蓋 [i,ri)[i,r_i)。顯然這是把平面上一個直角梯形區域 {(x,y)x[i,ri),yxli}\{(x,y)|x\in[i,r_i),y\geq x-l_i\} 全部變爲 alia_{l_i},容易發現,如果我們從下到上進行覆蓋操作,實際覆蓋的區間中所有元素值是一樣的,可以變爲直角梯形上的加法。

直角梯形上的加法可以通過差分轉化爲直角三角形上的加法。

於是現在問題就是平面上一個形如 {(x,y)xa,yxb}\{(x,y)|x\geq a,y\geq x-b\} 的區域的加法,同時在某個 y=ty=t 的直線上詢問 xpx\leq p的位置之和。

從下到上掃描線,考慮修改對詢問的貢獻,假設修改爲 (a,b,c)(a,b,c),其中 a,ba,b 含義如上所示, cc 表示權值。詢問爲 (t,p)(t,p)

容易發現貢獻即爲 c(min(t+b,p)min(p,a1))c\cdot(\min(t+b,p)-\min(p,a-1))

tt 在一加一減前綴差分中貢獻沒了,只剩下 c(min(b,pt)min(p,a1))c\cdot(\min(b,p-t)-\min(p,a-1))

樹狀數組維護即可。


#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++; 
	}template<typename T>T get_integer(){
		char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
		while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
	}inline int gi(){return get_integer<int>();}
}using namespace IO;

using std::cerr;
using std::cout;

cs int N=2e5+7;

int n,Q;
ll ans[N];

struct BIT{
	ll a[N+N];BIT(){}
	void add(int p,ll v){for(;p<=n+n;p+=p&-p)a[p]+=v;}
	ll qy(int p)cs{ll r=0;for(;p>0;p&=p-1)r+=a[p];return r;}
};

struct structure{
	BIT s,ss;structure(){}
	void add(int p,int v){p+=n;s.add(1,v),s.add(p+1,-v),ss.add(p+1,(ll)p*v);}
	ll qy(int p)cs{p+=n;return s.qy(p)*p+ss.qy(p);}
}t1,t2;

int a[N];ll sm[N];
int l[N],r[N];
int st[N],tp;

struct atom{int p,v;};
std::vector<atom> vec[N];
struct Qry{int l,r,id;};
std::vector<Qry> q[N];

void Main(){
	n=gi(),Q=gi();
	for(int re i=1;i<=n;++i)
		a[i]=gi(),sm[i]=sm[i-1]+a[i];
	for(int re i=1;i<=Q;++i){
		int t=gi(),l=gi(),r=gi();
		ans[i]=sm[r]-sm[l-1];
		q[t].push_back({l-1,r,i});
	}for(int re i=1;i<=n;++i){
		while(tp&&a[i]>=a[st[tp]])
			r[st[tp--]]=i;
		l[i]=st[tp];st[++tp]=i;
	}for(int re i=1;i<=n;++i)
		if(l[i]){
			vec[i-l[i]].push_back({i,a[l[i]]-a[i]});
			if(r[i])vec[r[i]-l[i]].push_back({r[i],a[i]-a[l[i]]});
		}
	for(int re i=1;i<=n;++i){
		for(auto &t:vec[i])
			t1.add(t.p-i,t.v),t2.add(t.p-1,t.v);
		for(auto &t:q[i])
			ans[t.id]+=t1.qy(t.r-i)-t2.qy(t.r)
				-t1.qy(t.l-i)+t2.qy(t.l);
	}for(int re i=1;i<=Q;++i)cout<<ans[i]<<"\n";
}

inline void file(){
#ifdef zxyoi
	freopen("fire.in","r",stdin);
#endif
}signed main(){file();Main();return 0;}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章