csp 模擬 八雲藍【計數】【線段樹】

傳送門

其實跟線段樹沒什麼關係。

對於這道題,我們發現直接計數複雜度很大。比起對於每個詢問,計算有多少個區間被調用,不如對於每個區間,計算有哪些詢問調用了它。

對於一個詢問,我們直接上線段樹。然後接下來大力分類討論。

1.不相交

不相交還做個鬼,直接跳過。

2.相交但不覆蓋

對於一個相交但不覆蓋,不容斥的話,分左右兩種。容斥的話,就用總區間數減去不相交數。

我選擇的不容斥(很難寫,不推薦),很明顯,只需要左右手玩一下等差數列就行,小心計算。

3.覆蓋。

爲了複雜度的保證,我們在覆蓋後應該直接獲得這個點的答案然後返回。

但事實上,這個點在線段樹上所對應的子樹中,每個區間都可能成爲終止區間或者經過區間,這些我們不能一一獲取。

所以只能預處理。對於每個點,預處理其子樹中,每一個點作爲終止區間或者經過區間的答案和。

①:作爲終止區間

一個點作爲終止區間,需要滿足兩個條件:其被覆蓋,其父親不被覆蓋。

只計算其被覆蓋,很簡單:(l-ql+1)(qr-r+1)

對於第二個條件,我們容斥轉換爲:其被覆蓋的方案-其父親被覆蓋的方案。

然後我們發現,除了葉子節點,一個線段樹結點有兩個兒子。所以它會被減兩次。而它自己會加一次。所以總的來說,它實際上是做了一個負的貢獻。

所以我們得到公式:如果是葉子節點,貢獻爲(l-ql+1)(qr-r+1),反之,在公式的基礎上*-1。

②:作爲經過區間

一個點作爲經過區間,需要滿足兩個條件:與詢問相交,不被詢問覆蓋。

有兩種計算方式。一種是直接大力計算,一種是容斥計算。

大力計算:將方案分成兩種:一個端點在l,r之外,和兩個端點都在l,r之內。

對於一個端點在l,r之外,我們發現另一個端點能取的範圍是[l,r-1]或者[l+1,r],所以每取一次貢獻是(r-l),總貢獻(r-l)(l-ql+1+qr-r+1)

對於都在端點內,實際上是兩個點的範圍是[l+1,r-1],等價於問一個長度爲r-l-1的區間有多少個子區間,用等差數列計算得\frac{(r-l)(r-l-1)}{2}

加起來得貢獻爲(r-l)(l-ql+qr-r+2)+\frac{(r-l)(r-l-1)}{2}

 

容斥計算:方案爲總方案-不相交的-覆蓋的。

總方案:\frac{(qr-ql+1)(qr-ql+2)}{2},左邊不相交的:\frac{(l-ql)(l-ql+1)}{2},右邊不相交的:\frac{(qr-r)(qr-r+1)}{2},覆蓋的:(l-ql+1)(qr-r+1)

合起來是\frac{(qr-ql+1)(qr-ql+2)}{2}-\frac{(l-ql)(l-ql+1)}{2}-\frac{(qr-r)(qr-r+1)}{2}-(l-ql+1)(qr-r+1)

 

完了,然後加起來。。

因爲我的計算能力太弱算錯了共計5次,用了三個小時調試,,

代碼不能看,奇醜無比。

#include<bits/stdc++.h>
using namespace std;
#define in read()
#define int long long
int in{
	int cnt=0,f=1;char ch=0;
	while(!isdigit(ch)){
		ch=getchar();if(ch=='-')f=-1;
	}
	while(isdigit(ch)){
		cnt=cnt*10+ch-48;ch=getchar();
	}return cnt*f;
}
struct node{
	int l,r;
	int L,R,LR,gu;
}t[2000003];
void build(int u,int l,int r){
//	cout<<u<<" "<<l<<" "<<r<<endl;
	t[u].l=l;t[u].r=r;
	if(l==r){
		t[u].L=r-1;t[u].R=l+1;t[u].LR=-1;t[u].gu=1-l*r+l-r;
		t[u].L*=2;t[u].R*=2;t[u].LR*=2;t[u].gu*=2;
//		cout<<l<<" "<<r<<" "<<t[u].L<<" "<<t[u].R<<" "<<t[u].LR<<" "<<t[u].gu<<endl; 
		return;
	}int mid=(l+r)>>1;
	t[u].L=1-r;t[u].R=-1-l;t[u].LR=1;t[u].gu=l*r-1+r-l;
	t[u].L*=2;t[u].R*=2;t[u].LR*=2;t[u].gu*=2;
//	cout<<l<<" "<<r<<" "<<t[u].L<<" "<<t[u].R<<" "<<t[u].LR<<" "<<t[u].gu<<endl;
	t[u].L+=2*l-2*r;t[u].R+=2*r-2*l;t[u].gu+=4*l*r-2*l*l-2*r*r-4*l+4*r+r*r-2*r*l+l*l-r+l;

//	cout<<l<<" "<<r<<" "<<t[u].L<<" "<<t[u].R<<" "<<t[u].LR<<" "<<t[u].gu<<endl;
	build(u*2,l,mid);build(u*2+1,mid+1,r);
	
	t[u].L+=t[u*2].L+t[u*2+1].L;
	t[u].R+=t[u*2].R+t[u*2+1].R;
	t[u].LR+=t[u*2].LR+t[u*2+1].LR;
	t[u].gu+=t[u*2].gu+t[u*2+1].gu;
//	cout<<l<<" "<<r<<" "<<t[u].L<<" "<<t[u].R<<" "<<t[u].LR<<" "<<t[u].gu<<endl;
}
int query(int u,int ql,int qr){
//	cout<<u<<" "<<t[u].l<<" "<<t[u].r<<endl;
	int tmp=0;
	int mid=(t[u].l+t[u].r)>>1;
	
	if(ql<=t[u].l&&t[u].r<=qr){
//		cout<<t[u].l<<" "<<t[u].r<<" "<<t[u].L<<" "<<t[u].R<<" "<<t[u].LR<<" "<<t[u].gu<<" "<<t[u].x<<" "<<t[u*2].x<<" "<<t[u*2+1].x<<endl;
		return t[u].L*ql+t[u].R*qr+t[u].LR*ql*qr+t[u].gu;
	}if(t[u].l!=t[u].r){
		if(qr>=t[u].l&&ql<t[u].l&&qr<t[u].r){tmp+=(qr+t[u].l-2*ql+2)*(qr-t[u].l+1);}
		if(ql<=t[u].r&&qr>t[u].r&&ql>t[u].l){tmp+=(t[u].r-ql+1)*(2*qr-t[u].r-ql+2);}
//		if(ql<=t[u].l&&t[u].r<=qr){cout<<"o5p ";tmp+=t[u].r-t[u].l+(t[u].r-t[u].l)*(t[u].r-t[u].l+1)/2;}
		if(ql>=t[u].l&&qr<t[u].r||ql>t[u].l&&qr<=t[u].r){tmp+=(qr-ql+2)*(qr-ql+1);}
//		cout<<t[u].l<<" "<<t[u].r<<" "<<tmp<<endl;
	}
	if(ql<=mid)tmp+=query(u*2,ql,qr);
	if(mid<qr)tmp+=query(u*2+1,ql,qr);
	return tmp;
}
int n,q,op;
int last;
signed main(){
	n=in;q=in;op=in;build(1,1,n); 
	while(q--){
		int l=in;int r=in;
		l=(l^(last*op))%n+1;
		r=(r^(last*op))%n+1;
		if(l>r)swap(l,r);
//		cout<<l<<" "<<r<<endl;
		last=query(1,l,r)/2;
		cout<<last<<'\n';
	}
	return 0;
}

 

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