其實跟線段樹沒什麼關係。
對於這道題,我們發現直接計數複雜度很大。比起對於每個詢問,計算有多少個區間被調用,不如對於每個區間,計算有哪些詢問調用了它。
對於一個詢問,我們直接上線段樹。然後接下來大力分類討論。
1.不相交
不相交還做個鬼,直接跳過。
2.相交但不覆蓋
對於一個相交但不覆蓋,不容斥的話,分左右兩種。容斥的話,就用總區間數減去不相交數。
我選擇的不容斥(很難寫,不推薦),很明顯,只需要左右手玩一下等差數列就行,小心計算。
3.覆蓋。
爲了複雜度的保證,我們在覆蓋後應該直接獲得這個點的答案然後返回。
但事實上,這個點在線段樹上所對應的子樹中,每個區間都可能成爲終止區間或者經過區間,這些我們不能一一獲取。
所以只能預處理。對於每個點,預處理其子樹中,每一個點作爲終止區間或者經過區間的答案和。
①:作爲終止區間
一個點作爲終止區間,需要滿足兩個條件:其被覆蓋,其父親不被覆蓋。
只計算其被覆蓋,很簡單:。
對於第二個條件,我們容斥轉換爲:其被覆蓋的方案-其父親被覆蓋的方案。
然後我們發現,除了葉子節點,一個線段樹結點有兩個兒子。所以它會被減兩次。而它自己會加一次。所以總的來說,它實際上是做了一個負的貢獻。
所以我們得到公式:如果是葉子節點,貢獻爲,反之,在公式的基礎上*-1。
②:作爲經過區間
一個點作爲經過區間,需要滿足兩個條件:與詢問相交,不被詢問覆蓋。
有兩種計算方式。一種是直接大力計算,一種是容斥計算。
大力計算:將方案分成兩種:一個端點在l,r之外,和兩個端點都在l,r之內。
對於一個端點在l,r之外,我們發現另一個端點能取的範圍是或者,所以每取一次貢獻是(r-l),總貢獻
對於都在端點內,實際上是兩個點的範圍是,等價於問一個長度爲r-l-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;
}