Comet OJ - Contest #14(set區間操作 + 樹狀數組)

Comet OJ - Contest #14

在這裏插入圖片描述

做法

這題是一個很騷的做法。

因爲每次是把整個區間覆蓋爲某個數字,所以可以看作是把一段區間內的很多段數字合併成一個的過程。

我們考慮用setset去維護這個過程,setset裏面保存四元組(l,r,x,id)(l,r,x,id),表示區間[l,r][l,r]都是xx且是在第idid個操作之後改變的。setsetrr爲關鍵字排序。同時,用一個樹狀數組表示執行了前ii個操作之後的所有數字總和,每次維護每個操作之後總和的變化。

顯然,在一開始的時候,整個setset裏面只有(1,m,0,0)(1,m,0,0),即全部是0。對於一個操作(l,r,x)(l,r,x),首先找到包含ll的那個區間,假設爲[L,R][L,R],把它分爲[L,l1][L,l-1][l,R][l,R]兩個區間,對rr也做相同的操作。這樣做了之後,就會出現以ll爲左端點的區間和以rr爲右端點的區間,這樣接下來我們就可以確定setset內這兩個區間之間的所有區間。而這些區間就是我們需要去掉的,直接遍歷一遍,在樹狀數組裏面把每個區間對應的idid及其之後的所有總和減去(rl+1)x(r-l+1)*x。最後,再把當前區間對應的四元組(l,r,x,id)(l,r,x,id)加入,對應的在樹狀數組的idid及其之後都加上相應的和即可。

接下來我們來考慮一下複雜度。對於每個操作,我們最多會拆分左右兩個區間,對應增加兩個四元組,總共就是2n2n個四元組。而對於每個四元組,我們最多會在setset中插入和刪除一次,每次插入和刪除的複雜度是O(logN)O(logN),這裏的複雜度就是O(NlogN)O(NlogN)。然後,每個操作,我們會在set裏面找其左右端點對應的位置,這裏每次也是O(logN)O(logN)的,總時間複雜度不會邊。最後就是樹狀數組的修改,對應每個四元組最多兩次修改,因此整個過程下來複雜度還是O(NlogN)O(NlogN)

代碼

#include<bits/stdc++.h>
#define fi first
#define se second
#define LL long long
#define pb push_back
#define lb lower_bound
#define INF 0x3f3f3f3f
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)

using namespace std;

const int N=1000010;

struct Operation
{
	int l,r,x,id;

	bool operator < (const Operation &a) const
	{
		return a.r>r;
	}

} op[N];

struct query{int l,r,id;} q[N];

set<Operation> st;
LL c[N],ans[N];
int n,m,Q;

inline void update(int x,LL y)
{
    if (x==0) return;
	for(int i=x;i<N;i+=i&-i)
		c[i]+=y;
}

inline LL getsum(int x)
{
	LL res=0;
	for(int i=x;i;i-=i&-i)
		res+=c[i];
	return res;
}

inline bool cmp(query a,query b)
{
	return a.r<b.r;
}

inline void split(set<Operation>::iterator it,int x)
{
    if (x<it->l||x>it->r) return;
	int l=it->l,r=it->r;
	int v=it->x,id=it->id;
	st.erase(it);
	st.insert({l,x,v,id});
	st.insert({x+1,r,v,id});
}

int main()
{
	sccc(n,m,Q);
	for(int i=1;i<=n;i++)
		sccc(op[i].l,op[i].r,op[i].x);
	for(int i=1;i<=Q;i++)
	{
		scc(q[i].l,q[i].r);
		q[i].id=i;
	}
	st.insert({1,m,0,0});
	sort(q+1,q+1+Q,cmp);
	for(int i=1;i<=Q;i++)
	{
		for(int j=q[i-1].r+1;j<=q[i].r;j++)
		{
			int l=op[j].l,r=op[j].r,x=op[j].x;
			auto L=st.lb({0,l-1,0,0});
			split(L,l-1);
			auto R=st.lb({0,r,0,0});
			split(R,r);
			L=st.lb({0,l,0,0});
			R=st.lb({0,r+1,0,0});
			while(L!=R)
			{
				auto cur=L; L++;
				update(cur->id,(LL)-(cur->r-cur->l+1)*cur->x);
				st.erase(cur);
			}
			update(j,(LL)(r-l+1)*x);
			st.insert({l,r,x,j});
		}
		ans[q[i].id]=getsum(q[i].r)-getsum(q[i].l-1);
	}
	for(int i=1;i<=Q;i++)
		printf("%lld\n",ans[i]);
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章