其实跟线段树没什么关系。
对于这道题,我们发现直接计数复杂度很大。比起对于每个询问,计算有多少个区间被调用,不如对于每个区间,计算有哪些询问调用了它。
对于一个询问,我们直接上线段树。然后接下来大力分类讨论。
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;
}