# 基础模板

## 常用板子

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template<class T>inline void MAX(T &x,T y){if(y>x)x=y;}
template<class T>inline void MIN(T &x,T y){if(y<x)x=y;}
template<class T>inline void rd(T &x){
x=0;char o,f=1;
while(o=getchar(),o<48)if(o==45)f=-f;
do x=(x<<3)+(x<<1)+(o^48);
while(o=getchar(),o>47);
x*=f;
}
template<class T>inline void print(T x,bool op=1){
static int top,stk[105];
if(x<0)x=-x,putchar('-');
if(x==0)putchar('0');
while(x)stk[++top]=x%10,x/=10;
while(top)putchar(stk[top--]+'0');
putchar(op?'\n':' ');
}

signed main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif

return (0-0);
}


## 数学题常用板子

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template<class T>inline void MAX(T &x,T y){if(y>x)x=y;}
template<class T>inline void MIN(T &x,T y){if(y<x)x=y;}
template<class T>inline void rd(T &x){
x=0;char o,f=1;
while(o=getchar(),o<48)if(o==45)f=-f;
do x=(x<<3)+(x<<1)+(o^48);
while(o=getchar(),o>47);
x*=f;
}
template<class T>inline void print(T x,bool op=1){
static int top,stk[105];
if(x<0)x=-x,putchar('-');
if(x==0)putchar('0');
while(x)stk[++top]=x%10,x/=10;
while(top)putchar(stk[top--]+'0');
putchar(op?'\n':' ');
}
const int SIZE=1e6+5;
const int P=1e9+7;
//const int P=998244353;
int fac[SIZE],inv[SIZE];
int fast(int a,int b=P-2){
int res=1;
while(b){
if(b&1)res=1ll*res*a%P;
a=1ll*a*a%P;
b>>=1;
}
return res;
}
int comb(int n,int m){
if(n<0||m<0||n<m)return 0;
return 1ll*fac[n]*inv[m]%P*inv[n-m]%P;
}
ll gcd(ll n,ll m){
if(m==0)return n;
return gcd(m,n%m);
}

signed main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
fac[0]=1;
for(int i=1;i<SIZE;i++)fac[i]=1ll*fac[i-1]*i%P;
inv[SIZE-1]=fast(fac[SIZE-1]);
for(int i=SIZE-2;i>=0;i--)inv[i]=1ll*inv[i+1]*(i+1)%P;

return (0-0);
}


## 输出挂

template<class T>inline void print(T x,bool op=1){
static int top,stk[105];
if(x<0)x=-x,putchar('-');
if(x==0)putchar('0');
while(x)stk[++top]=x%10,x/=10;
while(top)putchar(stk[top--]+'0');
putchar(op?'\n':' ');
}


int Tot;
char buf[50000005],*ch=buf-1;
template<class T>inline void rd(T &x){
int f=1;
for(++ch;*ch<48&&ch<buf+Tot;++ch)if(*ch==45)f=-f;
if(ch==buf+Tot)return;
for(x=0;*ch>47;++ch)x=(x<<3)+(x<<1)+(*ch^48);
x*=f;
}



## 高精度

struct INT{
static const int base=1e4;
vector<int>v;
INT(ll x=0){
while(x)v.push_back(x%base),x/=base;
if(v.size()==0)v.push_back(0);
}
inline int size()const{return v.size();}
inline void resize(int sz=1){v.clear();v.resize(sz,0);}
inline void topzero(){while(v.size()>1&&v.back()==0)v.pop_back();}
INT operator +(const INT &A)const{
INT T;
T.resize(max(size(),A.size())+1);
for(int i=0;i<T.size()-1;i++){
if(i<size())T.v[i]+=v[i];
if(i<A.size())T.v[i]+=A.v[i];
if(T.v[i]>=base)T.v[i+1]++,T.v[i]-=base;
}
T.topzero();
return T;
}
INT operator *(const INT &A)const{
INT T;
T.resize(size()+A.size()+5);
for(int i=0;i<size();i++)for(int j=0;j<A.size();j++){
T.v[i+j]+=v[i]*A.v[j];
if(T.v[i+j]>=2e9)T.v[i+j+1]+=T.v[i+j]/base,T.v[i+j]%=base;
}
for(int i=0;i<T.size();i++){
if(T.v[i]>=base)T.v[i+1]+=T.v[i]/base,T.v[i]%=base;
}
T.topzero();
return T;
}
INT operator /(const int x)const{
INT res;
res.resize(size());
ll tmp=0;
for(int i=size()-1;i>=0;i--){
tmp=tmp*base+v[i];
res.v[i]=tmp/x,tmp%=x;
}
res.topzero();
return res;
}
int operator %(const int mod)const{
int res=0;
for(int i=size()-1;i>=0;i--)res=(1ll*res*base+v[i])%mod;
return res;
}
friend INT fast(INT a,ll b){
INT res(1);
while(b){
if(b&1)res=res*a;
a=a*a;
b>>=1;
}
res.topzero();
return res;
}
string str;
cin>>str;
resize();
for(int i=0;i<str.size();i++)*this=*this*INT(10)+INT(str[i]-'0');
}
void print(){
printf("%d",v[size()-1]);
for(int i=size()-2;i>=0;i--)printf("%04d",v[i]);
}
};


## 分数类

struct frac{
#define ll __int128
ll a,b;
frac(ll _a=0,ll _b=1):a(_a),b(_b){}
ll gcd(ll a,ll b){
if(b==0)return a;
return gcd(b,a%b);
}
frac work(ll a,ll b){
if(b<0)a=-a,b=-b;
ll tmp=gcd(a,b);
if(tmp<0)tmp=-tmp;
return (frac){a/tmp,b/tmp};
}
frac operator +(frac A){return work(a*A.b+b*A.a,b*A.b);}
frac operator -(frac A){return work(a*A.b-b*A.a,b*A.b);}
frac operator *(frac A){return work(a*A.a,b*A.b);}
frac operator *(ll x){return work(a*x,b);}
frac operator /(frac A){return work(a*A.b,b*A.a);}
frac operator /(ll x){return work(a,b*x);}
void pt(ll x){
static int stk[105],top;
if(x==0)printf("0");
if(x<0)x=-x,printf("-");
while(x)stk[++top]=x%10,x/=10;
while(top)printf("%d",stk[top--]);
}
void pt(){
pt(a),printf("/"),pt(b),printf("  ");
}
#undef ll
};


## 打表压缩

/*
利用44进制对打表的数组进行压缩，大于等于44的数表示分隔
对于int范围以内的数压缩效果较好
减号 '-'(45) 表示负号
去掉 '"'(34) '/'(47) '?'(63) '\'(92)
少了 ' '(32) '~'(126)
*/
void encode(ll *A,char *str,int n){
for(int i=1,len=0;i<=n;i++){
ll x=A[i];
if(x<0)str[len++]='-',x=-x;
if(x==0)str[len++]=81;
while(x){
int t=x%44;
x/=44;
if(x==0)t+=44;
str[len++]=t+33+(t>=1)+(t>=11)+(t>=12)+(t>=27)+(t>=55);
}
}
}
int decode(char *str,ll *A){
int n=0,len=strlen(str);
for(int l=0,r=0,f=1;l<len;l=r+1,r=l,f=1){
if(str[l]=='-')f=-1,l++,r++;
while(r+1<len&&str[r]<81)r++;
ll x=0;
for(int i=r;i>=l;i--){
int t=str[i]-33;
t-=(t>=1),t-=(t>=11),t-=(t>=12),t-=(t>=27),t-=(t>=55);
x=x*44+(t%44);
}
A[++n]=f*x;
}
return n;
}


/*
利用89进制对打表的数组进行压缩，最大限度利用可显示字符
对于long long范围以内的大整数压缩效果较好
空格 ' '(32) 表示数字分隔符
减号 '-'(45) 表示负号
去掉 '"'(34) '/'(47) '?'(63) '\'(92)
*/
void encode(ll *A,char *str,int n){
for(int i=1,len=0;i<=n;i++){
ll x=A[i];
if(x<0)str[len++]='-',x=-x;
if(x==0)str[len++]=33;
while(x){
int t=x%89;
x/=89;
str[len++]=t+33+(t>=1)+(t>=11)+(t>=12)+(t>=27)+(t>=55);
}
str[len++]=' ';
}
}
int decode(char *str,ll *A){
int n=0,len=strlen(str);
for(int l=0,r=0,f=1;l<len;l=r+2,r=l,f=1){
if(str[l]=='-')f=-1,l++,r++;
while(r+1<len&&str[r+1]!=' ')r++;
ll x=0;
for(int i=r;i>=l;i--){
int t=str[i]-33;
t-=(t>=1),t-=(t>=11),t-=(t>=12),t-=(t>=27),t-=(t>=55);
x=x*89+t;
}
A[++n]=f*x;
}
return n;
}


## 基数排序

void Sort(int n,int *A){
static const int M=1e6+5,S=16,P=(1<<S)-1;
static int cnt[1<<S],B[M];
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++)cnt[A[i]&P]++;
for(int i=1;i<=P;i++)cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)B[cnt[A[i]&P]--]=A[i];
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++)cnt[B[i]>>S]++;
for(int i=1;i<=P;i++)cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)A[cnt[B[i]>>S]--]=B[i];
}


## 杂项

priority_queue<int,vector<int>,greater<int> >Q;


inline int popcount(unsigned int x){
return __builtin_popcount(x);
}

inline int popcountll(unsigned long long x){
return __builtin_popcountll(x);
}


__builtin_ctzll(0)的返回值为64

inline int ctzll(unsigned long long x){
return __builtin_ctzll(x);
}


O(length)选出中点并将数组分成两边，使k左边的元素小等于等A[k]，使k右边的元素大于等于A[k]

	nth_element(A+l,A+k,A+r+1);


	merge(A+1,A+n+1,B+1,B+m+1,C+1,cmp);

	inplace_merge(A+l,A+mid+1,A+r+1,cmp);


rng()返回一个unsigned int范围内的随机数，搭配取模使用

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());


mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

shuffle(A+1,A+n+1,rng);

	srand(time(NULL));
random_shuffle(A+1,A+n+1);//n大时打乱效果不佳


inline double run_time(){
return 1.0*clock()/CLOCKS_PER_SEC;
}


	prev_permutation(A+1,A+n+1);//上一个排列
next_permutation(A+1,A+n+1);//下一个排列

    for(int i=1;i<=n;i++)A[i]=i;
do{

}while(next_permutation(A+1,A+n+1));//枚举所有排列


string转char

	string str="jiedai";
puts(str.data());


# 数据结构

## 树状数组

### 一维树状数组

int limit;//树状数组下标的上界
while(x<=limit)bit[x]+=v,x+=x&-x;
}
ll query(ll *bit,int x){
ll res=0;
while(x)res+=bit[x],x-=x&-x;
return res;
}


∑ i = 1 r a [ i ] = ∑ i = 1 r ∑ j = 1 i d [ j ] \sum\limits_{i=1}^{r}{a[i]}=\sum\limits_{i=1}^{r}{\sum\limits_{j=1}^{i}{d[j]}} = ∑ i = 1 r ( r + 1 − i ) ∗ d [ i ] =\sum\limits_{i=1}^{r}{(r+1-i)*d[i]} = ( r + 1 ) ∗ ∑ i = 1 r d [ i ] − ∑ i = 1 r d [ i ] ∗ i =(r+1)*\sum\limits_{i=1}^{r}{d[i]}-\sum\limits_{i=1}^{r}{d[i]*i}

b i t 1 bit1 来维护 d [ i ] d[i] 的前缀和，用 b i t 2 bit2 来维护 d [ i ] ∗ i d[i]*i 的前缀和

void real_add(ll *bit1,ll *bit2,int l,int r,ll v){
}
ll real_query(ll *bit1,ll *bit2,int l,int r){
return (r+1)*query(bit1,r)-query(bit2,r)-l*query(bit1,l-1)+query(bit2,l-1);
}

struct BIT{
static const int SIZE=1e6+5;
ll bit1[SIZE],bit2[SIZE];
int limit;
void init(int n=SIZE-1){
limit=n;
for(int i=1;i<=n;i++)bit1[i]=bit2[i]=0;
}
BIT(){init();}
while(x<=limit)bit[x]+=v,x+=x&-x;
}
ll query(ll *bit,int x){
ll res=0;
while(x)res+=bit[x],x-=x&-x;
return res;
}
}
ll query(int l,int r){
return (r+1)*query(bit1,r)-query(bit2,r)-l*query(bit1,l-1)+query(bit2,l-1);
}
}bit;


int sum_lower_bound(ll *bit,ll v){
int pos=0;
for(int i=20;i>=0;i--)
if(pos+(1<<i)<=limit&&bit[pos+(1<<i)]<v)
v-=bit[pos|=1<<i];
return pos+1;
}


### 二维树状数组

const int SIZE=5005;
int limit1=SIZE-1,limit2=SIZE-1;//两个维度的下标的上界
ll bit[SIZE][SIZE];

for(int i=x;i<=limit1;i+=i&-i)
for(int j=y;j<=limit2;j+=j&-j)
bit[i][j]+=v;
}
ll query(int x,int y){
ll res=0;
for(int i=x;i;i-=i&-i)
for(int j=y;j;j-=j&-j)
res+=bit[i][j];
return res;
}


∑ i = 1 x ∑ j = 1 y a [ i ] [ j ] = ∑ i = 1 x ∑ j = 1 y ∑ k = 1 i ∑ t = 1 j d [ k ] [ t ] \sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}{a[i][j]}=\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}\sum\limits_{k=1}^{i}\sum\limits_{t=1}^{j}{d[k][t]} = ∑ i = 1 x ∑ j = 1 y ( x + 1 − i ) ( y + 1 − j ) d [ i ] [ j ] =\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}{(x+1-i)(y+1-j)d[i][j]}

= ( x + 1 ) ( y + 1 ) ∑ i = 1 x ∑ j = 1 y d [ i ] [ j ] =(x+1)(y+1)\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}{d[i][j]} − ( y + 1 ) ∑ i = 1 x ∑ j = 1 y d [ i ] [ j ] ∗ i -(y+1)\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}{d[i][j]*i} − ( x + 1 ) ∑ i = 1 x ∑ j = 1 y d [ i ] [ j ] ∗ j -(x+1)\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}{d[i][j]*j} + ∑ i = 1 x ∑ j = 1 y d [ i ] [ j ] ∗ i j +\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}{d[i][j]*ij}

const int SIZE=5005;
int limit1=SIZE-1,limit2=SIZE-1;//两个维度的下标的上界
ll bit1[SIZE][SIZE],bit2[SIZE][SIZE],bit3[SIZE][SIZE],bit4[SIZE][SIZE];

for(int i=x;i<=limit1;i+=i&-i)for(int j=y;j<=limit2;j+=j&-j){
bit1[i][j]+=v;
bit2[i][j]+=v*x;
bit3[i][j]+=v*y;
bit4[i][j]+=v*x*y;
}
}
ll query(int x,int y){
ll res=0;
for(int i=x;i;i-=i&-i)
for(int j=y;j;j-=j&-j)
res+=(x+1)*(y+1)*bit1[i][j]-(y+1)*bit2[i][j]-(x+1)*bit3[i][j]+bit4[i][j];
return res;
}
void real_add(int x1,int y1,int x2,int y2,ll v){
}
ll real_query(int x1,int y1,int x2,int y2){
return query(x2,y2)-query(x1-1,y2)-query(x2,y1-1)+query(x1-1,y1-1);
}


## 线段树

const int SIZE=1e6+5;
int LLL=1,RRR=SIZE-5;

ll tree[SIZE<<2],lazy[SIZE<<2];
void build(int l=LLL,int r=RRR,int p=1){
lazy[p]=0;
if(l==r){
tree[p]=0;
return;
}
int mid=l+r>>1;
build(l,mid,p<<1);
build(mid+1,r,p<<1|1);
tree[p]=tree[p<<1]+tree[p<<1|1];
}
void down(int p,int l,int r){
if(lazy[p]){
int mid=l+r>>1;
lazy[p<<1]+=lazy[p];
lazy[p<<1|1]+=lazy[p];
tree[p<<1]+=1ll*(mid-l+1)*lazy[p];
tree[p<<1|1]+=1ll*(r-mid)*lazy[p];
lazy[p]=0;
}
}
void update(int a,int b,ll v,int l=LLL,int r=RRR,int p=1){
if(l>b||r<a)return;
if(l>=a&&r<=b){
tree[p]+=1ll*(r-l+1)*v;
lazy[p]+=v;
return;
}
down(p,l,r);
int mid=l+r>>1;
update(a,b,v,l,mid,p<<1);
update(a,b,v,mid+1,r,p<<1|1);
tree[p]=tree[p<<1]+tree[p<<1|1];
}
ll query(int a,int b,int l=LLL,int r=RRR,int p=1){
if(l>b||r<a)return 0;
if(l>=a&&r<=b)return tree[p];
down(p,l,r);
int mid=l+r>>1;
return query(a,b,l,mid,p<<1)+query(a,b,mid+1,r,p<<1|1);
}


const int SIZE=1e6+5;
int LLL=1,RRR=SIZE-5;

ll tree[SIZE<<2],lazy[SIZE<<2];
void build(int l=LLL,int r=RRR,int p=1){
lazy[p]=0;
if(l==r){
tree[p]=0;
return;
}
int mid=l+r>>1;
build(l,mid,p<<1);
build(mid+1,r,p<<1|1);
tree[p]=max(tree[p<<1],tree[p<<1|1]);
}
void down(int p){
if(lazy[p]){
tree[p<<1]+=lazy[p];
lazy[p<<1]+=lazy[p];
tree[p<<1|1]+=lazy[p];
lazy[p<<1|1]+=lazy[p];
lazy[p]=0;
}
}
void update(int a,int b,ll v,int l=LLL,int r=RRR,int p=1){
if(l>b||r<a)return;
if(l>=a&&r<=b){
tree[p]+=v;
lazy[p]+=v;
return;
}
down(p);
int mid=l+r>>1;
update(a,b,v,l,mid,p<<1);
update(a,b,v,mid+1,r,p<<1|1);
tree[p]=max(tree[p<<1],tree[p<<1|1]);
}
ll query(int a,int b,int l=LLL,int r=RRR,int p=1){
if(l>b||r<a)return 0;//
if(l>=a&&r<=b)return tree[p];
down(p);
int mid=l+r>>1;
return max(query(a,b,l,mid,p<<1),query(a,b,mid+1,r,p<<1|1));
}


int query_pos(int a,int b,int v,int l=LLL,int r=RRR,int p=1){
if(l>b||r<a)return 0;
if(l==r)return l;
down(p);
int mid=l+r>>1,res=0;
if(tree[p<<1]>=v)res=query_pos(a,b,v,l,mid,p<<1);
if(res==0&&tree[p<<1|1]>=v)res=query_pos(a,b,v,mid+1,r,p<<1|1);
return res;
}


const int M=1e5+5;
void build(int l=1,int r=n,int p=1){
lazy_mul[p]=1;
if(l==r){
tree[p]=A[l]%mod;
return;
}
int mid=l+r>>1;
build(l,mid,p<<1);
build(mid+1,r,p<<1|1);
tree[p]=(tree[p<<1]+tree[p<<1|1])%mod;
}
void down(int p,int l,int r){
if(lazy_mul[p]!=1){
tree[p<<1]=1ll*tree[p<<1]*lazy_mul[p]%mod;
lazy_mul[p<<1]=1ll*lazy_mul[p<<1]*lazy_mul[p]%mod;
tree[p<<1|1]=1ll*tree[p<<1|1]*lazy_mul[p]%mod;
lazy_mul[p<<1|1]=1ll*lazy_mul[p<<1|1]*lazy_mul[p]%mod;
lazy_mul[p]=1;
}
int mid=l+r>>1;
}
}
void update(int a,int b,int mul,int add,int l=1,int r=n,int p=1){
if(l>b||r<a)return;
if(l>=a&&r<=b){
if(mul!=1){
tree[p]=1ll*tree[p]*mul%mod;
lazy_mul[p]=1ll*lazy_mul[p]*mul%mod;
}
}
return;
}
down(p,l,r);
int mid=l+r>>1;
tree[p]=(tree[p<<1]+tree[p<<1|1])%mod;
}
int query(int a,int b,int l=1,int r=n,int p=1){
if(l>b||r<a)return 0;
if(l>=a&&r<=b)return tree[p];
down(p,l,r);
int mid=l+r>>1;
return (query(a,b,l,mid,p<<1)+query(a,b,mid+1,r,p<<1|1))%mod;
}


## 主席树

const int SIZE=2e5+5;
const int TREE=SIZE*20;
int n,q,uni,A[SIZE],tmp[SIZE];
int trid,rt[SIZE],ls[TREE],rs[TREE],sum[TREE];
void update(int &now,int lst,int x,int l=1,int r=uni){
now=++trid;
ls[now]=ls[lst],rs[now]=rs[lst],sum[now]=sum[lst]+1;
if(l==r)return;
int mid=l+r>>1;
if(x<=mid)update(ls[now],ls[lst],x,l,mid);
else update(rs[now],rs[lst],x,mid+1,r);
}
int query(int left,int right,int k,int l=1,int r=uni){
if(l==r)return l;
int mid=l+r>>1,res=sum[ls[right]]-sum[ls[left]];
if(k<=res)return query(ls[left],ls[right],k,l,mid);
else return query(rs[left],rs[right],k-res,mid+1,r);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
rd(n),rd(q);
for(int i=1;i<=n;i++)rd(A[i]),tmp[++uni]=A[i];
sort(tmp+1,tmp+1+uni);
uni=unique(tmp+1,tmp+1+uni)-tmp-1;
for(int i=1;i<=n;i++)A[i]=lower_bound(tmp+1,tmp+1+uni,A[i])-tmp;
for(int i=1;i<=n;i++)update(rt[i],rt[i-1],A[i]);
while(q--){
int l,r,k;
rd(l),rd(r),rd(k);
printf("%d\n",tmp[query(rt[l-1],rt[r],k)]);
}
return (0-0);
}


## 线段树合并/裂开

P5494

0 p x y ：将可重集 p 中大于等于 x 且小于等于 y 的值放入一个新的可重集中

1 p t ：将可重集 t 中的数并入可重集 p，且删除可重集 t

2 p x q ：在可重集 p 中加入 x 个数字 q

3 p x y ：查询可重集 p 中大于等于 x 且小于等于 y 的值的个数

4 p k ：查询可重集 p 中第 k 小的数，不存在输出 -1

const int SIZE=2e5+5;
const int TREE=SIZE*32;
int LLL=1,RRR=SIZE-5;

int trid,rub,rt[SIZE],bin[TREE],ls[TREE],rs[TREE];
ll cnt[TREE];
int new_node(){
if(rub)return bin[rub--];
else return ++trid;
}
void del_node(int p){
ls[p]=rs[p]=cnt[p]=0;
bin[++rub]=p;
}
void update(int &p,int x,int v,int l=LLL,int r=RRR){
if(p==0)p=new_node();
cnt[p]+=v;
if(l==r)return;
int mid=l+r>>1;
if(x<=mid)update(ls[p],x,v,l,mid);
else update(rs[p],x,v,mid+1,r);
}
ll query_cnt(int p,int a,int b,int l=LLL,int r=RRR){
if(p==0||l>b||r<a)return 0;
if(l>=a&&r<=b)return cnt[p];
int mid=l+r>>1;
return query_cnt(ls[p],a,b,l,mid)+query_cnt(rs[p],a,b,mid+1,r);
}
int query_kth(int p,ll k,int l=LLL,int r=RRR){
if(l==r)return l;
int mid=l+r>>1;
if(k<=cnt[ls[p]])return query_kth(ls[p],k,l,mid);
else return query_kth(rs[p],k-cnt[ls[p]],mid+1,r);
}
int merge(int x,int y){
if(!x||!y)return x|y;
cnt[x]+=cnt[y];
ls[x]=merge(ls[x],ls[y]);
rs[x]=merge(rs[x],rs[y]);
del_node(y);
return x;
}
void split(int x,int &y,ll k){
if(x==0)return;
y=new_node();
ll v=cnt[ls[x]];
if(k<=v)swap(rs[x],rs[y]);
if(k<v)split(ls[x],ls[y],k);
if(k>v)split(rs[x],rs[y],k-v);
cnt[y]=cnt[x]-k;
cnt[x]=k;
}

int n,q,all;
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
rd(n),rd(q);
rt[all=1]=new_node();
for(int i=1;i<=n;i++){
int x;
rd(x);
update(rt[1],i,x);
}
while(q--){
int op,p,x,y;
ll k;
rd(op);
if(op==0){
rd(p),rd(x),rd(y);
ll k1=query_cnt(rt[p],1,y);
ll k2=query_cnt(rt[p],x,y);
int tmp;
split(rt[p],rt[++all],k1-k2);
split(rt[all],tmp,k2);
rt[p]=merge(rt[p],tmp);
}
else if(op==1){
rd(p),rd(y);
rt[p]=merge(rt[p],rt[y]);
}
else if(op==2){
rd(p),rd(x),rd(y);
update(rt[p],y,x);
}
else if(op==3){
rd(p),rd(x),rd(y);
printf("%lld\n",query_cnt(rt[p],x,y));
}
else if(op==4){
rd(p),rd(k);
if(k<=0||k>query_cnt(rt[p],1,n))puts("-1");
else printf("%d\n",query_kth(rt[p],k));
}
}
return (0-0);
}


## 吉司机线段树

Segment Tree Beats!

BZOJ4695

1 L R v ：给区间[L,R]加上一个数v

2 L R v ：把区间[L,R]中小于v的数变成v

3 L R v ：把区间[L,R]中大于v的数变成v

4 L R ：求区间[L,R]的和

5 L R ：求区间[L,R]的最大值

6 L R ：求区间[L,R]的最小值

const int SIZE=5e5+5;
const ll INF=1e18;

int n;
ll A[SIZE];

int len[SIZE<<2],cmx[SIZE<<2],cmi[SIZE<<2];
ll sum[SIZE<<2],mx[SIZE<<2],mx2[SIZE<<2],mi[SIZE<<2],mi2[SIZE<<2];
void up(int p){
int ls=(p<<1),rs=(p<<1|1);
sum[p]=sum[ls]+sum[rs];
if(mx[ls]>mx[rs]){
mx[p]=mx[ls];
mx2[p]=max(mx2[ls],mx[rs]);
cmx[p]=cmx[ls];
}
else if(mx[ls]<mx[rs]){
mx[p]=mx[rs];
mx2[p]=max(mx[ls],mx2[rs]);
cmx[p]=cmx[rs];
}
else{
mx[p]=mx[ls];
mx2[p]=max(mx2[ls],mx2[rs]);
cmx[p]=cmx[ls]+cmx[rs];
}
if(mi[ls]<mi[rs]){
mi[p]=mi[ls];
mi2[p]=min(mi2[ls],mi[rs]);
cmi[p]=cmi[ls];
}
else if(mi[ls]>mi[rs]){
mi[p]=mi[rs];
mi2[p]=min(mi[ls],mi2[rs]);
cmi[p]=cmi[rs];
}
else{
mi[p]=mi[ls];
mi2[p]=min(mi2[ls],mi2[rs]);
cmi[p]=cmi[ls]+cmi[rs];
}
}
void work_max(int p,ll v){
lazy_mx[p]+=v;
if(mx[p]==mi[p])mi[p]+=v;
else if(cmx[p]+cmi[p]==len[p])mi2[p]+=v;
mx[p]+=v;
sum[p]+=1ll*v*cmx[p];
}
void work_min(int p,ll v){
lazy_mi[p]+=v;
if(mi[p]==mx[p])mx[p]+=v;
else if(cmi[p]+cmx[p]==len[p])mx2[p]+=v;
mi[p]+=v;
sum[p]+=1ll*v*cmi[p];
}
mx[p]+=v;
mi[p]+=v;
if(mx2[p]!=-INF)mx2[p]+=v;
if(mi2[p]!=INF)mi2[p]+=v;
sum[p]+=1ll*v*len[p];
}
void down(int p){
int ls=(p<<1),rs=(p<<1|1);
if(lazy_mx[p]){
if(mx[ls]>mx[rs])work_max(ls,lazy_mx[p]);
else if(mx[ls]<mx[rs])work_max(rs,lazy_mx[p]);
else work_max(ls,lazy_mx[p]),work_max(rs,lazy_mx[p]);
lazy_mx[p]=0;
}
if(lazy_mi[p]){
if(mi[ls]<mi[rs])work_min(ls,lazy_mi[p]);
else if(mi[ls]>mi[rs])work_min(rs,lazy_mi[p]);
else work_min(ls,lazy_mi[p]),work_min(rs,lazy_mi[p]);
lazy_mi[p]=0;
}
}
}
void build(int l=1,int r=n,int p=1){
len[p]=r-l+1;
if(l==r){
mx[p]=mi[p]=sum[p]=A[l];
mx2[p]=-INF,mi2[p]=INF;
cmx[p]=cmi[p]=1;
return;
}
int mid=l+r>>1;
build(l,mid,p<<1);
build(mid+1,r,p<<1|1);
up(p);
}
void update_max(int a,int b,ll v,int l=1,int r=n,int p=1){
if(l>b||r<a)return;
if(l>=a&&r<=b){
if(v<=mi[p])return;
else if(v<mi2[p]){
work_min(p,v-mi[p]);
return;
}
}
down(p);
int mid=l+r>>1;
update_max(a,b,v,l,mid,p<<1);
update_max(a,b,v,mid+1,r,p<<1|1);
up(p);
}
void update_min(int a,int b,ll v,int l=1,int r=n,int p=1){
if(l>b||r<a)return;
if(l>=a&&r<=b){
if(v>=mx[p])return;
else if(v>mx2[p]){
work_max(p,v-mx[p]);
return;
}
}
down(p);
int mid=l+r>>1;
update_min(a,b,v,l,mid,p<<1);
update_min(a,b,v,mid+1,r,p<<1|1);
up(p);
}
void update_add(int a,int b,ll v,int l=1,int r=n,int p=1){
if(l>b||r<a)return;
if(l>=a&&r<=b){
return;
}
down(p);
int mid=l+r>>1;
up(p);
}
ll query_max(int a,int b,int l=1,int r=n,int p=1){
if(l>b||r<a)return -INF;
if(l>=a&&r<=b)return mx[p];
down(p);
int mid=l+r>>1;
return max(query_max(a,b,l,mid,p<<1),query_max(a,b,mid+1,r,p<<1|1));
}
ll query_min(int a,int b,int l=1,int r=n,int p=1){
if(l>b||r<a)return INF;
if(l>=a&&r<=b)return mi[p];
down(p);
int mid=l+r>>1;
return min(query_min(a,b,l,mid,p<<1),query_min(a,b,mid+1,r,p<<1|1));
}
ll query_sum(int a,int b,int l=1,int r=n,int p=1){
if(l>b||r<a)return 0;
if(l>=a&&r<=b)return sum[p];
down(p);
int mid=l+r>>1;
return query_sum(a,b,l,mid,p<<1)+query_sum(a,b,mid+1,r,p<<1|1);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
int q,op,l,r;
ll v;
rd(n);
for(int i=1;i<=n;i++)rd(A[i]);
build();
rd(q);
while(q--){
rd(op),rd(l),rd(r);
if(0-0);
else if(op==2)rd(v),update_max(l,r,v);
else if(op==3)rd(v),update_min(l,r,v);
else if(op==4)printf("%lld\n",query_sum(l,r));
else if(op==5)printf("%lld\n",query_max(l,r));
else if(op==6)printf("%lld\n",query_min(l,r));
}
return (0-0);
}


## 可撤销并查集

struct disjoint_set{
static const int SIZE=1e6+5;
int top,stk[SIZE],fa[SIZE],sz[SIZE];
inline void init(int n){
top=0;
for(int i=1;i<=n;i++)fa[i]=i,sz[i]=1;
}
inline int getfa(int x){
while(x!=fa[x])x=fa[x];
return x;
}
x=getfa(x),y=getfa(y);
if(x!=y){
if(sz[x]>sz[y])swap(x,y);
stk[++top]=x;
fa[x]=y;
sz[y]+=sz[x];
return 1;
}
else return 0;
}
inline void pop_to(int tar){
while(top>tar){
sz[fa[stk[top]]]-=sz[stk[top]];
fa[stk[top]]=stk[top];
top--;
}
}
}U;


## ST表

const int M=1e5+5;
int n,q,A[M],st[M][20];
inline int ST(int l,int r){
int k=log2(r-l+1);
return max(st[l][k],st[r-(1<<k)+1][k]);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
rd(n),rd(q);
for(int i=1;i<=n;i++)rd(A[i]);
for(int i=1;i<=n;i++)st[i][0]=A[i];
for(int i=n;i>=1;i--)
for(int j=1;i+(1<<j)-1<=n;j++)
st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1]);
while(q--){
int l,r;
rd(l),rd(r);
printf("%d\n",ST(l,r));
}
return (0-0);
}


## 分块

struct BLOCK1{
static const int M=2e5+5;
static const int S=400;
static const int K=M/S+5;
int bel[M],L[K],R[K];
ll big[K],small[M];
BLOCK1(){
for(int i=1;i<M;i++)bel[i]=(i-1)/S+1;
for(int i=1;i<K;i++)L[i]=1+(i-1)*S,R[i]=i*S;
}
void update(int x,ll v){
for(int i=1;i<=bel[x];i++)big[i]+=v;
for(int i=L[bel[x]];i<=x;i++)small[i]+=v;
}
ll query(int l,int r){
return big[bel[l]+1]+small[l]-big[bel[r+1]+1]-small[r+1];
}
}block1;


struct BLOCK2{
static const int M=2e5+5;
static const int S=400;
static const int K=M/S+5;
int bel[M],L[K],R[K];
ll big[K],small[M];
BLOCK2(){
for(int i=1;i<M;i++)bel[i]=(i-1)/S+1;
for(int i=1;i<K;i++)L[i]=1+(i-1)*S,R[i]=i*S;
}
void update(int x,ll v){
big[bel[x]]+=v;
small[x]+=v;
}
ll query(int l,int r){
ll res=0;
if(bel[l]!=bel[r]){
for(int i=l;i<=R[bel[l]];i++)res+=small[i];
for(int i=bel[l]+1;i<bel[r];i++)res+=big[i];
for(int i=L[bel[r]];i<=r;i++)res+=small[i];
}
else for(int i=l;i<=r;i++)res+=small[i];
return res;
}
}block2;


struct BLOCK3{
static const int M=2e5+5;
static const int S=400;
static const int K=M/S+5;
int bel[M],L[K],R[K];
ll big[K],small[M];
BLOCK3(){
for(int i=1;i<M;i++)bel[i]=(i-1)/S+1;
for(int i=1;i<K;i++)L[i]=1+(i-1)*S,R[i]=i*S;
}
void update(int l,int r,ll v){
if(bel[l]!=bel[r]){
for(int i=l;i<=R[bel[l]];i++)small[i]+=v;
for(int i=bel[l]+1;i<bel[r];i++)big[i]+=v;
for(int i=L[bel[r]];i<=r;i++)small[i]+=v;
}
else for(int i=l;i<=r;i++)small[i]+=v;
}
ll query(int x){
return big[bel[x]]+small[x];
}
}block3;


struct BLOCK4{
static const int M=2e5+5;
static const int S=400;
static const int K=M/S+5;
int bel[M],L[K],R[K];
ll big[K],small[M];
BLOCK4(){
for(int i=1;i<M;i++)bel[i]=(i-1)/S+1;
for(int i=1;i<K;i++)L[i]=1+(i-1)*S,R[i]=i*S;
}
void update(int l,int r,ll v){
big[bel[l]]+=v,small[l]+=v;
big[bel[r+1]]-=v,small[r+1]-=v;
}
ll query(int x){
ll res=0;
for(int i=0;i<bel[x];i++)res+=big[i];
for(int i=L[bel[x]];i<=x;i++)res+=small[i];
return res;
}
}block4;


## 莫队

### 普通莫队

const int M=1e6+5;
const int S=sqrt(M);
struct node{
int l,r,id;
bool operator <(const node &A)const{
if(l/S!=A.l/S)return l/S<A.l/S;
if(l/S&1)return r<A.r;
else return r>A.r;
}
}Q[M];
int n,m,res,A[M],ans[M],cnt[M];
void insert(int x){
cnt[x]++;
if(cnt[x]==1)res++;
}
void erase(int x){
cnt[x]--;
if(cnt[x]==0)res--;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
rd(n);
for(int i=1;i<=n;i++)rd(A[i]);
rd(m);
for(int i=1;i<=m;i++)rd(Q[i].l),rd(Q[i].r),Q[i].id=i;
sort(Q+1,Q+1+m);
int l=1,r=0;
for(int i=1;i<=m;i++){
while(r<Q[i].r)insert(A[++r]);
while(l>Q[i].l)insert(A[--l]);
while(r>Q[i].r)erase(A[r--]);
while(l<Q[i].l)erase(A[l++]);
ans[Q[i].id]=res;
}
for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
return (0-0);
}


### 带修莫队

1. 修改某个位置的数字

2. 求区间 [ l , r ] [l,r] 之间有多少种不同的数字

1. 修改数的移动：

1. 左端点的移动：

1. 右端点的移动：

S = n 2 / 3 S=n^{2/3} 时，最优复杂度为 n 5 / 3 n^{5/3}

const int M=140005;
const int S=2500;
const int K=1e6+5;
struct node{
int l,r,t,id;
bool operator <(const node &A)const{
if(l/S!=A.l/S)return l/S<A.l/S;
if(r/S!=A.r/S){
if(l/S&1)return r/S<A.r/S;
else return r/S>A.r/S;
}
if(r/S&1)return t<A.t;
else return t>A.t;
}
}Q[M];
char str[M][5];
int n,m,A[M],pos[M],X[M],Y[M],ans[M];
int res,cnt[K];
void insert(int x){
cnt[x]++;
if(cnt[x]==1)res++;
}
void erase(int x){
cnt[x]--;
if(cnt[x]==0)res--;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
rd(n),rd(m);
for(int i=1;i<=n;i++)rd(A[i]);
int l=1,r=0,now=0,tot=0;
for(int i=1;i<=m;i++){
scanf("%s",str[i]);
int a,b;
rd(a),rd(b);
if(str[i][0]=='R'){
now++;
pos[now]=a;
X[now]=A[a];
Y[now]=b;
A[a]=b;
}
else Q[++tot]=(node){a,b,now,i};
}
sort(Q+1,Q+1+tot);
for(int i=1;i<=tot;i++){
while(now<Q[i].t){
now++;
A[pos[now]]=Y[now];
if(pos[now]>=l&&pos[now]<=r)erase(X[now]),insert(Y[now]);
}
while(now>Q[i].t){
A[pos[now]]=X[now];
if(pos[now]>=l&&pos[now]<=r)erase(Y[now]),insert(X[now]);
now--;
}
while(r<Q[i].r)insert(A[++r]);
while(l>Q[i].l)insert(A[--l]);
while(r>Q[i].r)erase(A[r--]);
while(l<Q[i].l)erase(A[l++]);
ans[Q[i].id]=res;
}
for(int i=1;i<=m;i++)if(str[i][0]=='Q')printf("%d\n",ans[i]);
return (0-0);
}


### 树上带修莫队

n n 个节点的树， m m 种颜色， q q 个询问。

i i 次遍历到某种颜色的点，该点的价值乘上对应的数值。
0 0 x x y y : 把 x x 点的颜色改为 y y
1 1 x x y y : 查询遍历 x x y y 路径的总价值。

1. 如果 x x y y 就是 l c a lca ，那么这条链对应的括号序区间为 i d L [ x ] idL[x] i d L [ y ] idL[y]
2. 如果 x x y y 都不是 l c a lca ，那么对应的区间为 i d R [ x ] idR[x] i d L [ y ] idL[y] 。不过这个区间中并没有包含 l c a lca 这个点，这需要在处理询问的时候特别加上。

const int M=1e5+5;
const int S=2000;
to[++tot]=b;
}
int fa[M],son[M],sz[M],deep[M],top[M];
int reid[M<<1],idL[M],idR[M],dfsid;
void dfs(int x,int f){
fa[x]=f;
sz[x]=1;
deep[x]=deep[f]+1;
reid[idL[x]=++dfsid]=x;
int y=to[i];
if(y==f)continue;
dfs(y,x);
sz[x]+=sz[y];
if(sz[y]>sz[son[x]])son[x]=y;
}
reid[idR[x]=++dfsid]=x;
}
void chain(int x,int k){
top[x]=k;
if(son[x])chain(son[x],k);
int y=to[i];
if(y==fa[x]||y==son[x])continue;
chain(y,y);
}
}
int LCA(int a,int b){
while(top[a]!=top[b]){
if(deep[top[a]]<deep[top[b]])swap(a,b);
a=fa[top[a]];
}
return deep[a]<deep[b]?a:b;
}
struct node{
int l,r,t,lca,id;
bool operator <(const node &A)const{
if(l/S!=A.l/S)return l/S<A.l/S;
if(r/S!=A.r/S){
if(l/S%2)return r/S<A.r/S;
else return r/S>A.r/S;
}
if(r/S%2)return t<A.t;
else return t>A.t;
}
}Q[M];
int op0,op1,X[M],Y[M],Z[M],mark[M],cnt[M];
ll res,ans[M];
mark[x]^=1;
if(mark[x])res+=1ll*A[C[x]]*B[++cnt[C[x]]];
else res-=1ll*A[C[x]]*B[cnt[C[x]]--];
}
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
rd(n),rd(m),rd(q);
for(int i=1;i<=m;i++)rd(A[i]);
for(int i=1;i<=n;i++)rd(B[i]);
for(int i=1;i<n;i++){
int a,b;
rd(a),rd(b);
}
for(int i=1;i<=n;i++)rd(C[i]);
dfs(1,0);
chain(1,1);
int now=0;
for(int i=1;i<=q;i++){
int op,x,y;
rd(op),rd(x),rd(y);
if(op==0){
X[++op0]=x,Y[op0]=y,Z[op0]=C[x];
C[x]=y,now++;
}
else{
int lca=LCA(x,y);
if(idL[x]>idL[y])swap(x,y);
if(x==lca||y==lca)Q[++op1]=(node){idL[x],idL[y],op0,0,i};
else Q[++op1]=(node){idR[x],idL[y],op0,lca,i};
}
}
sort(Q+1,Q+1+op1);
int L=Q[1].l,R=L-1;
for(int i=1;i<=op1;i++){
int l=Q[i].l,r=Q[i].r,t=Q[i].t,lca=Q[i].lca,id=Q[i].id;
while(now<t){
now++;
if(mark[X[now]]){
C[X[now]]=Y[now];
}
else C[X[now]]=Y[now];
}
while(now>t){
if(mark[X[now]]){
C[X[now]]=Z[now];
}
else C[X[now]]=Z[now];
now--;
}
ans[id]=res;
}
for(int i=1;i<=q;i++)if(ans[i])printf("%lld\n",ans[i]);
return (0-0);
}


### 回滚莫队

loj 2874

[ l , r ] [l,r] 中一种数权值与出现次数之积的最大值

const int M=2e5+5;
const int S=350;
struct node{
int l,r,id;
bool operator <(const node &A)const{
if(l/S!=A.l/S)return l/S<A.l/S;
return r<A.r;
}
}Q[M];
int n,m,A[M],B[M],cnt[M],cnt2[M];
ll res,ans[M];

int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
rd(n),rd(m);
for(int i=1;i<=n;i++)rd(A[i]),B[i]=A[i];
sort(B+1,B+1+n);
int uni=unique(B+1,B+1+n)-B-1;
for(int i=1;i<=n;i++)A[i]=lower_bound(B+1,B+1+uni,A[i])-B;
for(int i=1;i<=m;i++){
int l,r;
rd(l),rd(r);
Q[i]=(node){l,r,i};
}
sort(Q+1,Q+1+m);
int L=0,R=0;
for(int i=1;i<=m;i++){
if(i==1||Q[i].l/S!=Q[i-1].l/S){
L=min(n+1,Q[i].l/S*S+S);
R=L-1;
res=0;
for(int j=1;j<=uni;j++)cnt[j]=0;
}
int l=Q[i].l,r=Q[i].r,id=Q[i].id;
if(l/S==r/S){
ll tmp=0;
for(int j=l;j<=r;j++)MAX(tmp,1ll*B[A[j]]*(++cnt2[A[j]]));
for(int j=l;j<=r;j++)cnt2[A[j]]=0;
ans[id]=tmp;
}
else{
while(R<r)++R,MAX(res,1ll*B[A[R]]*(++cnt[A[R]]));
ll tmp=res;
for(int j=l;j<L;j++)MAX(tmp,1ll*B[A[j]]*(++cnt[A[j]]));
for(int j=l;j<L;j++)cnt[A[j]]--;
ans[id]=tmp;
}
}
for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
return (0-0);
}


### 二次离线莫队

( [ l 1 , r 1 ] , [ l 2 , r 2 ] ) ([l1,r1],[l2,r2]) 表示一个点在 [ l 1 , r 1 ] [l1,r1] 中另外一点在 [ l 2 , r 2 ] [l2,r2] 中的逆序对数

const int M=1e5+5;
const int S=sqrt(M);
const int K=M/S+5;
struct BLOCK{
int bel[M],L[K],R[K],big[K],small[M];
BLOCK(){
for(int i=1;i<M;i++)bel[i]=(i-1)/S+1;
for(int i=1;i<K;i++)L[i]=1+(i-1)*S,R[i]=i*S;
}
void clear(){
memset(big,0,sizeof(big));
memset(small,0,sizeof(small));
}
void update(int x,int v){
for(int i=1;i<=bel[x];i++)big[i]+=v;
for(int i=L[bel[x]];i<=x;i++)small[i]+=v;
}
int query(int l,int r){
return big[bel[l]+1]+small[l]-big[bel[r+1]+1]-small[r+1];
}
}block;
struct node{
int l,r,id;
bool operator <(const node &A)const{
if(l/S!=A.l/S)return l/S<A.l/S;
if(l/S&1)return r<A.r;
else return r>A.r;
}
}Q[M];
int n,m,uni,A[M],tmp[M],bit[M];
ll ans[M],sum1[M],sum2[M],res1[M],res2[M];
vector<node>vi1[M],vi2[M];
while(x<=uni)bit[x]+=v,x+=x&-x;
}
int query(int x){
int res=0;
while(x)res+=bit[x],x-=x&-x;
return res;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
rd(n),rd(m);
for(int i=1;i<=n;i++)rd(A[i]),tmp[i]=A[i];
for(int i=1;i<=m;i++)rd(Q[i].l),rd(Q[i].r),Q[i].id=i;
sort(tmp+1,tmp+1+n);
uni=unique(tmp+1,tmp+1+n)-tmp-1;
for(int i=1;i<=n;i++)A[i]=lower_bound(tmp+1,tmp+1+uni,A[i])-tmp;
sort(Q+1,Q+1+m);
for(int i=1,l=1,r=0;i<=m;i++){
if(Q[i].r>r)vi1[l-1].push_back((node){r+1,Q[i].r,i}),r=Q[i].r;
if(Q[i].l<l)vi2[r+1].push_back((node){Q[i].l,l-1,i}),l=Q[i].l;
if(Q[i].r<r)vi1[l-1].push_back((node){Q[i].r+1,r,i}),r=Q[i].r;
if(Q[i].l>l)vi2[r+1].push_back((node){l,Q[i].l-1,i}),l=Q[i].l;
}
memset(bit,0,sizeof(bit));
for(int i=1;i<=n;i++){
sum1[i]=sum1[i-1]+i-1-query(A[i]);
}
memset(bit,0,sizeof(bit));
for(int i=n;i>=1;i--){
sum2[i]=sum2[i+1]+query(A[i]-1);
}
block.clear();
for(int i=0;i<n;i++){
for(int j=0;j<vi1[i].size();j++){
int l=vi1[i][j].l,r=vi1[i][j].r,id=vi1[i][j].id;
for(int k=l;k<=r;k++)res1[id]+=block.query(A[k]+1,uni);
}
block.update(A[i+1],1);
}
block.clear();
for(int i=n+1;i>1;i--){
for(int j=0;j<vi2[i].size();j++){
int l=vi2[i][j].l,r=vi2[i][j].r,id=vi2[i][j].id;
for(int k=l;k<=r;k++)res2[id]+=block.query(1,A[k]-1);
}
block.update(A[i-1],1);
}
ll res=0;
for(int i=1,l=1,r=0;i<=m;i++){
if(Q[i].r>r)res+=sum1[Q[i].r]-sum1[r]-res1[i],r=Q[i].r;
if(Q[i].l<l)res+=sum2[Q[i].l]-sum2[l]-res2[i],l=Q[i].l;
if(Q[i].r<r)res-=sum1[r]-sum1[Q[i].r]-res1[i],r=Q[i].r;
if(Q[i].l>l)res-=sum2[l]-sum2[Q[i].l]-res2[i],l=Q[i].l;
ans[Q[i].id]=res;
}
for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
return (0-0);
}


## 线段树分治

P5787

int ans=0;
struct disjoint_set{
static const int SIZE=1e6+5;
int top,stk[SIZE],fa[SIZE],sz[SIZE];
int len[SIZE],dis[SIZE];
void init(int n){
top=0;
for(int i=1;i<=n;i++)fa[i]=i,sz[i]=1;
}
int getfa(int x){
if(fa[x]==x){dis[x]=0;return x;}
int f=getfa(fa[x]);
dis[x]=dis[fa[x]]+len[x];
return f;
}
int fx=getfa(x),fy=getfa(y);
if(fx!=fy){
if(sz[fx]>sz[fy])swap(x,y),swap(fx,fy);
stk[++top]=fx;
fa[fx]=fy;
sz[fy]+=sz[fx];
len[fx]=(dis[x]+dis[y]+1)%2;
}
else if(dis[x]%2==dis[y]%2)ans=0;
}
void pop_to(int tar){
while(top>tar){
sz[fa[stk[top]]]-=sz[stk[top]];
fa[stk[top]]=stk[top];
top--;
}
}
}U;

const int M=1e5+5;
int n,m,k;
vector<pair<int,int> >vi[M<<2];
void update(int a,int b,int x,int y,int l=1,int r=k,int p=1){
if(l>b||r<a)return;
if(l>=a&&r<=b){
vi[p].push_back(make_pair(x,y));
return;
}
int mid=l+r>>1;
update(a,b,x,y,l,mid,p<<1);
update(a,b,x,y,mid+1,r,p<<1|1);
}
void visit(int l=1,int r=k,int p=1){
int lst_top=U.top,lst_ans=ans;
if(l==r)puts(ans?"Yes":"No");
else{
int mid=l+r>>1;
visit(l,mid,p<<1);
visit(mid+1,r,p<<1|1);
}
U.pop_to(lst_top);
ans=lst_ans;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
rd(n),rd(m),rd(k);
for(int i=1;i<=m;i++){
int x,y,l,r;
rd(x),rd(y),rd(l),rd(r);
if(l<r)update(l+1,r,x,y);
}
ans=1;
U.init(n);
visit();
return (0-0);
}


## CDQ分治

P3810

### 三维偏序

const int M=1e5+5;
int n,k,X[M],Y[M],Z[M],rk[M],cnt[M],res[M],ans[M];
bool cmp(int a,int b){
if(X[a]!=X[b])return X[a]<X[b];
if(Y[a]!=Y[b])return Y[a]<Y[b];
return Z[a]<Z[b];
}
struct node{
int y,z,id;
bool operator <(const node &A)const{
if(y!=A.y)return y<A.y;
return id<A.id;
}
}Q[M];
int bit[M<<1];
while(x<=k)bit[x]+=v,x+=x&-x;
}
int query(int x){
int res=0;
while(x)res+=bit[x],x-=x&-x;
return res;
}
void CDQ(int l,int r){
if(l==r)return;
int mid=l+r>>1;
CDQ(l,mid),CDQ(mid+1,r);
sort(Q+l,Q+r+1);//可以用归并排序代替
for(int i=l;i<=r;i++){
else res[Q[i].id]+=query(Q[i].z);
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
rd(n),rd(k);
for(int i=1;i<=n;i++)rd(X[i]),rd(Y[i]),rd(Z[i]),rk[i]=i;
sort(rk+1,rk+1+n,cmp);
int uni=0;
for(int i=1;i<=n;i++){
if(X[rk[i]]!=X[rk[i-1]]||Y[rk[i]]!=Y[rk[i-1]]||Z[rk[i]]!=Z[rk[i-1]])
uni++,Q[uni]=(node){Y[rk[i]],Z[rk[i]],uni};
cnt[uni]++;
}
CDQ(1,uni);
for(int i=1;i<=uni;i++)ans[res[i]+cnt[i]-1]+=cnt[i];
for(int i=0;i<n;i++)printf("%d\n",ans[i]);
return (0-0);
}


### 四维偏序

HDOJ 5126

1. 向集合中加入三元组(a,b,c)

2. 查询集合中有多少个三元组(a,b,c)，满足a1<=a<=a2, b1<=b<=b2, c1<=c<=c2

1. 首先对第一维（即时间维）进行CDQ分治。对于分治区间 [ L , R ] [L,R] ，用左半区间的插入操作更新右半区间的查询操作。

2. 在这个区间内，再对第二维（即 a a 维）进行排序，排序的第二关键字为第一维（即时间维）。

再对这个区间内的第二维（即 a a 维）进行CDQ分治，用排序后 a a 维前一半小的操作去跟新 a a 维后一半的查询。

3. 在第二层的CDQ中，再对第三维（即 b b 维）进行排序，排序的第二关键字为第一维（即时间维），从小到大扫描每一个操作：

4. 对于修改操作，只有当这个操作满足第一维在第一层CDQ的左半区间且第二维在第二层CDQ的左半区间，
才将当前的第四维（即 c c 维）加入树状数组中。（第四维在预处理时需要离散）

5. 对于查询操作，只有当这个操作满足第一维在第一层CDQ的右半区间且第二维在第二层CDQ的右半区间，
才将当前的第四维在树状数组中查询，更新答案。

const int M=5e4+5;
const int LEFT=1;
const int RIGHT=2;
int cas,n,q,uni,OP[M],ans[M],A[M<<1],bit[M<<1];
while(x<=uni)bit[x]+=v,x+=x&-x;
}
int query(int x){
int res=0;
while(x)res+=bit[x],x-=x&-x;
return res;
}
struct node{
int id,x,y,z,op,part1,part2;
}Q[M<<3],tmp[M<<3];
bool cmp1(node &A,node &B){
if(A.x!=B.x)return A.x<B.x;
return A.id<B.id;
}
bool cmp2(node &A,node &B){
if(A.y!=B.y)return A.y<B.y;
return A.id<B.id;
}
void cdq(int l,int r){
if(l==r)return;
int mid=l+r>>1;
cdq(l,mid),cdq(mid+1,r);
for(int i=l;i<=mid;i++)Q[i].part2=LEFT;
for(int i=mid+1;i<=r;i++)Q[i].part2=RIGHT;
int a=l,b=mid+1,now=l;//使用归并排序提高效率
while(a<=mid&&b<=r){
if(cmp2(Q[a],Q[b]))tmp[now++]=Q[a++];
else tmp[now++]=Q[b++];
}
while(a<=mid)tmp[now++]=Q[a++];
while(b<=r)tmp[now++]=Q[b++];
for(int i=l;i<=r;i++)Q[i]=tmp[i];
//sort(Q+l,Q+r+1,cmp2);//用归并排序代替
for(int i=l;i<=r;i++){
if(Q[i].op!=0&&Q[i].part1==RIGHT&&Q[i].part2==RIGHT)ans[Q[i].id]+=Q[i].op*query(Q[i].z);
}
}
void CDQ(int l,int r){
if(l==r)return;
int mid=l+r>>1;
CDQ(l,mid),CDQ(mid+1,r);
for(int i=l;i<=mid;i++)Q[i].part1=LEFT;
for(int i=mid+1;i<=r;i++)Q[i].part1=RIGHT;
sort(Q+l,Q+r+1,cmp1);
cdq(l,r);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
rd(cas);
while(cas--){
n=uni=0;
memset(ans,0,sizeof(ans));
rd(q);
for(int i=1;i<=q;i++){
int x,y,z,x2,y2,z2;
rd(OP[i]),rd(x),rd(y),rd(z);
if(OP[i]==1){
Q[++n]=(node){i,x,y,z,0,0,0};
A[++uni]=z;
}
else{
rd(x2),rd(y2),rd(z2);
A[++uni]=z2;
A[++uni]=z-1;
Q[++n]=(node){i,x2,y2,z2,1,0,0};
Q[++n]=(node){i,x-1,y2,z2,-1,0,0};
Q[++n]=(node){i,x2,y-1,z2,-1,0,0};
Q[++n]=(node){i,x2,y2,z-1,-1,0,0};
Q[++n]=(node){i,x-1,y-1,z2,1,0,0};
Q[++n]=(node){i,x-1,y2,z-1,1,0,0};
Q[++n]=(node){i,x2,y-1,z-1,1,0,0};
Q[++n]=(node){i,x-1,y-1,z-1,-1,0,0};
}
}
sort(A+1,A+1+uni);
uni=unique(A+1,A+1+uni)-A-1;
for(int i=1;i<=n;i++)Q[i].z=lower_bound(A+1,A+1+uni,Q[i].z)-A;
CDQ(1,n);
for(int i=1;i<=q;i++)if(OP[i]==2)printf("%d\n",ans[i]);
}
return (0-0);
}


## Splay Tree

1. 插入 v
2. 删除 v (若有多个相同的数，只删除一个)
3. 查询 v 的排名 (比 v 小的数的个数 +1 )
4. 查询排名为 k 的数
5. 求小于 v 的最大的数
6. 求大于 v 的最小的数
struct Splay_Tree{
static const int SIZE=1e6+5;
int trid,root,par[SIZE],ch[SIZE][2],val[SIZE],cnt[SIZE],sz[SIZE];
void up(int x){
sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+cnt[x];
}
void UP(int x){
while(x)up(x),x=par[x];
}
void clean_node(int x){
par[x]=ch[x][0]=ch[x][1]=val[x]=cnt[x]=sz[x]=0;
}
void new_node(int &x,int f,int v){
x=++trid;
par[x]=f;
val[x]=v;
cnt[x]=sz[x]=1;
ch[x][0]=ch[x][1]=0;
UP(x);//单点插入时加上，建树时不加
}
void rotate(int x){
int y=par[x],s=ch[y][0]==x;
if(ch[x][s])par[ch[x][s]]=y;
ch[y][!s]=ch[x][s];
par[x]=par[y];
if(par[y])ch[par[y]][ch[par[y]][1]==y]=x;
par[y]=x;
ch[x][s]=y;
up(y),up(x);//order is important
}
void splay(int x,int goal=0){
while(par[x]!=goal){
int y=par[x];
if(par[y]!=goal){
if(ch[par[y]][0]==y^ch[y][0]==x)rotate(x);
else rotate(y);
}
rotate(x);
}
if(goal==0)root=x;
}
void insert(int v){
if(root==0){
new_node(root,0,v);
return;
}
for(int x=root;;x=ch[x][v>val[x]]){
if(v==val[x]){
cnt[x]++;
UP(x);
splay(x);
return;
}
if(ch[x][v>val[x]]==0){
new_node(ch[x][v>val[x]],x,v);
splay(ch[x][v>val[x]]);
return;
}
}
}
int get_pre(){
int x=ch[root][0];
if(x==0)return 0;
while(ch[x][1])x=ch[x][1];
return x;
}
int Find(int v){
for(int x=root;x;x=ch[x][v>val[x]])
if(v==val[x])return x;
return 0;
}
void erase(int v){
int x=Find(v);
if(x==0)return;
splay(x);
cnt[x]--,sz[x]--;
if(cnt[x]>0)return;
int y=get_pre();
if(y==0)par[root=ch[x][1]]=0;
else{
splay(y,x);
par[ch[x][1]]=y;
ch[y][1]=ch[x][1];
up(y);
par[root=y]=0;
}
clean_node(x);
}
int query_kth(int k){
int x=root;
while(1){
if(k<=sz[ch[x][0]])x=ch[x][0];
else if(k<=sz[ch[x][0]]+cnt[x]){splay(x);return val[x];}//将查询的点旋转到根，保证复杂度
else k-=sz[ch[x][0]]+cnt[x],x=ch[x][1];
}
}
int query_rank(int v){
int res=0,s=root;
for(int x=root;x;s=x,x=ch[x][v>val[x]]){
if(v==val[x]){res+=sz[ch[x][0]];break;}
if(v>val[x])res+=sz[ch[x][0]]+cnt[x];
}
splay(s);//将查询的点旋转到根，保证复杂度
return res+1;
}
int query_less(int v){
int res=-2e9,s=root;
for(int x=root;x;s=x,x=ch[x][v>val[x]])if(val[x]<v)MAX(res,val[x]);
splay(s);//将查询的点旋转到根，保证复杂度
return res;
}
int query_greater(int v){
int res=2e9,s=root;
for(int x=root;x;s=x,x=ch[x][v>=val[x]])if(val[x]>v)MIN(res,val[x]);
splay(s);//将查询的点旋转到根，保证复杂度
return res;
}
}splay;


struct Splay_Tree{
static const int SIZE=1e6+5;
int trid,root,par[SIZE],ch[SIZE][2],flip[SIZE],val[SIZE],sz[SIZE];
void up(int x){
sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;
}
void UP(int x){
while(x)up(x),x=par[x];
}
void down(int x){
if(flip[x]){
swap(ch[x][0],ch[x][1]);
if(ch[x][0])flip[ch[x][0]]^=1;
if(ch[x][1])flip[ch[x][1]]^=1;
flip[x]=0;
}
}
void DOWN(int x){
static int top,stk[SIZE];
while(x)stk[++top]=x,x=par[x];
while(top)down(stk[top--]);
}
void clean_node(int x){
par[x]=ch[x][0]=ch[x][1]=flip[x]=val[x]=sz[x]=0;
}
void new_node(int &x,int f,int v){
x=++trid;
par[x]=f;
val[x]=v;
sz[x]=1;
ch[x][0]=ch[x][1]=flip[x]=0;
//		UP(x);//单点插入时加上，建树时不加
}
void build(int &x,int f,int l,int r){
int mid=l+r>>1;
new_node(x,f,mid);
if(l<mid)build(ch[x][0],x,l,mid-1);
if(mid<r)build(ch[x][1],x,mid+1,r);
up(x);
}
void rotate(int x){
int y=par[x],s=ch[y][0]==x;
if(ch[x][s])par[ch[x][s]]=y;
ch[y][!s]=ch[x][s];
par[x]=par[y];
if(par[y])ch[par[y]][ch[par[y]][1]==y]=x;
par[y]=x;
ch[x][s]=y;
up(y),up(x);//order is important
}
void splay(int x,int goal=0){
while(par[x]!=goal){
int y=par[x];
if(par[y]!=goal){
if(ch[par[y]][0]==y^ch[y][0]==x)rotate(x);
else rotate(y);
}
rotate(x);
}
if(goal==0)root=x;
}
int Find_kth(int k){
int x=root;
while(1){
down(x);
if(k<=sz[ch[x][0]])x=ch[x][0];
else if(k<=sz[ch[x][0]]+1)return x;
else k-=sz[ch[x][0]]+1,x=ch[x][1];
}
}
void reverse(int l,int r){
if(l>=r)return;
int x=Find_kth(l-1),y=Find_kth(r+1);
splay(x),splay(y,x);
flip[ch[y][0]]^=1;
}
int query_kth(int k){
int x=root;
while(1){
down(x);
if(k<=sz[ch[x][0]])x=ch[x][0];
else if(k<=sz[ch[x][0]]+1){splay(x);return val[x];}//将查询的点旋转到根，保证复杂度
else k-=sz[ch[x][0]]+1,x=ch[x][1];
}
}
}splay;
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
int n,q,l,r;
rd(n),rd(q);
splay.build(splay.root,0,0,n+1);//将0和n+1插入方便提取区间
while(q--){
rd(l),rd(r);
splay.reverse(l+1,r+1);
}
for(int i=1;i<=n;i++)printf("%d%c",splay.query_kth(i+1),i==n?'\n':' ');
return (0-0);
}


struct Link_Cut_Tree{
static const int SIZE=1e5+5;
int par[SIZE],ch[SIZE][2],flip[SIZE],val[SIZE],sum[SIZE];
bool is_root(int x){//splay's root
return ch[par[x]][0]!=x&&ch[par[x]][1]!=x;
}
void up(int x){
sum[x]=val[x]^sum[ch[x][0]]^sum[ch[x][1]];
}
void down(int x){
if(flip[x]){
swap(ch[x][0],ch[x][1]);
if(ch[x][0])flip[ch[x][0]]^=1;
if(ch[x][1])flip[ch[x][1]]^=1;
flip[x]=0;
}
}
void DOWN(int x){
static int top,stk[SIZE];
for(stk[++top]=x;!is_root(x);x=par[x])stk[++top]=par[x];
while(top)down(stk[top--]);
}
void rotate(int x){
int y=par[x],s=ch[y][0]==x;
if(ch[x][s])par[ch[x][s]]=y;
ch[y][!s]=ch[x][s];
par[x]=par[y];
if(!is_root(y))ch[par[y]][ch[par[y]][1]==y]=x;
par[y]=x;
ch[x][s]=y;
up(y),up(x);//order is important
}
void splay(int x){
DOWN(x);
while(!is_root(x)){
int y=par[x];
if(!is_root(y)){
if(ch[par[y]][0]==y^ch[y][0]==x)rotate(x);
else rotate(y);
}
rotate(x);
}
up(x);
}
void Access(int x){
int s=0;
while(x){
splay(x);
ch[x][1]=s;
up(x);
x=par[s=x];
}
}
void make_root(int x){
Access(x);
splay(x);
flip[x]^=1;
}
void split(int x,int y){
make_root(x);
Access(y);
splay(y);
}
int find_root(int x){//tree's root
Access(x);
splay(x);
while(down(x),ch[x][0])x=ch[x][0];
splay(x);
return x;
}
make_root(x);
if(find_root(y)==x)return;
par[x]=y;
splay(x);
}
void cut(int x,int y){
if(find_root(x)!=find_root(y))return;
split(x,y);
if(par[x]!=y||ch[x][1])return;
par[x]=ch[y][0]=0;
up(y);
}
void update(int x,int v){
splay(x);
val[x]=v;
up(x);
}
int query(int x,int y){
split(x,y);
return sum[y];
}
}lct;





## KD Tree

1. K-D树：

以2_D树为例，树上的每个节点都“管理”着一个矩形区域，每个节点上存一个点的坐标，这个坐标一定位于这个节点“管理”的区域内，

接下来按照这个点的某一维（x或y）上的值将矩阵划分为两个子矩阵（对应由两个子节点“管理”）。

这样就将多维空间不断地通过某一维度的中位数点进行当前空间的分割，于是平衡时树的高度是 O ( l o g 2 n ) O(log_2{n}) 的。

KD树的查询其实是暴力+剪枝，复杂度可以当作 O ( n ) ) O(\sqrt{n}))

2. 需要维护的基础信息：

1. t r e e tree ：存储当前节点的坐标以及权值
2. l s o n , r s o n lson,rson ：当前节点的左右儿子
3. s u m sum ：当前子树中所有节点的权值和
4. s i z e size ：当前子树的节点数量
5. m i [ i ] mi[i] ：当前子树中第 i i 维的最小值
6. m x [ i ] mx[i] ：当前子树中第 i i 维的最大值
3. 常用基础函数：

1. nth_element(tmp+l,tmp+mid,tmp+r+1);用O(length)的时间选出中点并将数组分成两边
2. void rebuild(int &x,int l,int r);对子树进行重构
3. void to_array(int x);将子树中的所有节点储存入临时数组中
4. void check(int &x);检查当前子树是否平衡，平衡系数0.75
5. bool outside(int a1,int a2,int b1,int b2,int x1,int x2,int y1,int y2);判断当前区域(x1,y1)-(x2,y2)是否在查询区域(a1,b1)-(a2,b2)之外
6. bool inside(int a1,int a2,int b1,int b2,int x1,int x2,int y1,int y2);判断当前区域(x1,y1)-(x2,y2)是否在查询区域(a1,b1)-(a2,b2)之内
const int SIZE=2e5+5;
int TYPE,jiedai;
struct kd_node{
int dim[2],val;
bool operator <(const kd_node &A)const{
return dim[TYPE]<A.dim[TYPE];
}
}tree[SIZE],tmp[SIZE];
int root,trid,dfsid,rub,bin[SIZE];
int type[SIZE],ls[SIZE],rs[SIZE],mx[SIZE][2],mi[SIZE][2],sz[SIZE];
ll sum[SIZE];
void up(int x){
for(int i=0;i<2;i++){
mx[x][i]=mi[x][i]=tree[x].dim[i];
if(ls[x])MAX(mx[x][i],mx[ls[x]][i]),MIN(mi[x][i],mi[ls[x]][i]);
if(rs[x])MAX(mx[x][i],mx[rs[x]][i]),MIN(mi[x][i],mi[rs[x]][i]);
}
sum[x]=sum[ls[x]]+sum[rs[x]]+tree[x].val;
sz[x]=sz[ls[x]]+sz[rs[x]]+1;
}
void new_node(int &x,int ty,const kd_node &rhs){
if(rub)x=bin[rub--];
else x=++trid;
type[x]=ty;
tree[x]=rhs;
ls[x]=rs[x]=0;
up(x);
}
void clean_node(int x){
bin[++rub]=x;
ls[x]=rs[x]=sz[x]=sum[x]=0;
}
void rebuild(int &x,int l,int r){
int mid=l+r>>1;
TYPE=(++jiedai)&1;
nth_element(tmp+l,tmp+mid,tmp+r+1);
new_node(x,TYPE,tmp[mid]);
if(l<mid)rebuild(ls[x],l,mid-1);
if(mid<r)rebuild(rs[x],mid+1,r);
up(x);
}
void to_array(int x){
if(ls[x])to_array(ls[x]);
if(rs[x])to_array(rs[x]);
tmp[++dfsid]=tree[x];
clean_node(x);
}
void insert(int &x,const kd_node &rhs){
if(x==0){
new_node(x,(++jiedai)&1,rhs);
return;
}
if(rhs.dim[type[x]]<=tree[x].dim[type[x]])insert(ls[x],rhs);
else insert(rs[x],rhs);
up(x);
if(sz[ls[x]]>sz[x]*0.75||sz[rs[x]]>sz[x]*0.75){
dfsid=0;
to_array(x);
rebuild(x,1,dfsid);
}
}
ll query(int x,int a1,int a2,int b1,int b2){//a1<=x<=a2 b1<=y<=b2
if(x==0)return 0;
if(mi[x][0]>a2||mx[x][0]<a1||mi[x][1]>b2||mx[x][1]<b1)return 0;
if(mi[x][0]>=a1&&mx[x][0]<=a2&&mi[x][1]>=b1&&mx[x][1]<=b2)return sum[x];
if(tree[x].dim[0]>=a1&&tree[x].dim[0]<=a2&&tree[x].dim[1]>=b1&&tree[x].dim[1]<=b2)
return query(ls[x],a1,a2,b1,b2)+query(rs[x],a1,a2,b1,b2)+tree[x].val;
return query(ls[x],a1,a2,b1,b2)+query(rs[x],a1,a2,b1,b2);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
int n,op,x,y,v,x1,x2,y1,y2,lstans=0;
rd(n);
while(1){
rd(op);
if(op==1){
rd(x),rd(y),rd(v);
x^=lstans,y^=lstans,v^=lstans;
insert(root,(kd_node){x,y,v});
}
else if(op==2){
rd(x1),rd(y1),rd(x2),rd(y2);
x1^=lstans,y1^=lstans,x2^=lstans,y2^=lstans;
printf("%lld\n",lstans=query(root,x1,x2,y1,y2));
}
else if(op==3)break;
}
return (0-0);
}


1. 别问我为什么想到用K-D Tree的，因为是看了题解的。
2. 本题没有插入、删除等高级操作，仅仅建树和查询，代码简洁。
3. 进入正题：考虑暴力，暴力遍历对于每个点而言能形成的所有点对，显然复杂度为 O ( n 2 ) O(n^2) ，不可行，接下来考虑剪枝。
4. 首先， K = m i n ( 100 , n ∗ ( n + 1 ) 2 ) K=min(100,\frac{n*(n+1)}{2}) ，所以先在小顶堆中插入 2 ∗ K 2*K 0 0 ，在后续暴力搜索前 2 ∗ K 2*K 大点对的过程中逐渐把它们 p o p pop 掉。
5. 对这 N N 个点的每个点而言，都从K-D Tree的根节点往下遍历，每到一个节点，先计算当前节点与这个点的距离，并更新小顶堆，然后进入到剪枝的关键步骤。
6. 我们考虑每个节点的左右儿子，分别利用左右儿子的每个维度最大最小边界来计算可能的最远点，若当前子空间最远的点都无法对小顶堆进行更新，则不需要进入这个空间了！
7. 另一点剪枝：先遍历左右子空间中最远可能点更远的子空间，这样也许就不用再遍历另外一个空间啦。
8. 复杂度的话。。。还不会算，据说是 O ( n 3 2 ) O(n^{\frac{3}{2}})
const int SIZE=1e5+5;
int TYPE,jiedai;
struct kd_node{
int dim[2];
bool operator <(const kd_node &A)const{
return dim[TYPE]<A.dim[TYPE];
}
ll calc(kd_node &A){
return 1ll*(dim[0]-A.dim[0])*(dim[0]-A.dim[0])+1ll*(dim[1]-A.dim[1])*(dim[1]-A.dim[1]);
}
ll calc(int mx[2],int mi[2]){
int d1=max(abs(dim[0]-mx[0]),abs(dim[0]-mi[0]));
int d2=max(abs(dim[1]-mx[1]),abs(dim[1]-mi[1]));
return 1ll*d1*d1+1ll*d2*d2;
}
}tree[SIZE],tmp[SIZE];
int root,trid;
int type[SIZE],ls[SIZE],rs[SIZE],mx[SIZE][2],mi[SIZE][2];
void up(int x){
for(int i=0;i<2;i++){
mx[x][i]=mi[x][i]=tree[x].dim[i];
if(ls[x])MAX(mx[x][i],mx[ls[x]][i]),MIN(mi[x][i],mi[ls[x]][i]);
if(rs[x])MAX(mx[x][i],mx[rs[x]][i]),MIN(mi[x][i],mi[rs[x]][i]);
}
}
void new_node(int &x,int ty,const kd_node &rhs){
x=++trid;
type[x]=ty;
tree[x]=rhs;
ls[x]=rs[x]=0;
up(x);
}
void build(int &x,int l,int r){
int mid=l+r>>1;
TYPE=(++jiedai)&1;
nth_element(tmp+l,tmp+mid,tmp+r+1);
new_node(x,TYPE,tmp[mid]);
if(l<mid)build(ls[x],l,mid-1);
if(mid<r)build(rs[x],mid+1,r);
up(x);
}
priority_queue<ll,vector<ll>,greater<ll>>Q;
void query(int x,kd_node &rhs){
ll t=rhs.calc(tree[x]),t1=0,t2=0;
if(t>Q.top())Q.pop(),Q.push(t);
if(ls[x])t1=rhs.calc(mx[ls[x]],mi[ls[x]]);
if(rs[x])t2=rhs.calc(mx[rs[x]],mi[rs[x]]);
if(t1>t2){
if(t1>Q.top())query(ls[x],rhs);
if(t2>Q.top())query(rs[x],rhs);
}
else{
if(t2>Q.top())query(rs[x],rhs);
if(t1>Q.top())query(ls[x],rhs);
}
}
int n,k;
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
rd(n),rd(k);
for(int i=1;i<=n;i++)rd(tmp[i].dim[0]),rd(tmp[i].dim[1]);
build(root,1,n);
for(int i=1;i<=k+k;i++)Q.push(0);
for(int i=1;i<=n;i++)query(root,tree[i]);
printf("%lld\n",Q.top());
return (0-0);
}


## 左偏树*




## 双栈模拟队列

loj6515

const int M=5e4+5;
struct Stack{
int st,top;
pair<int,int>stk[M];
void insert(pair<int,int>x){
stk[++top]=x;
}
void pop(){
top--;
}
bool empty(){
return st==top;
}
}stk1,stk2;
int casid,m,p;
ll dp1[M][505],dp2[M][505],Q[M],A[M],B[M];
void DP(Stack &stk,ll dp[M][505],int i){
memset(dp[i],-1,sizeof(dp[i]));
for(int j=0;j<p;j++)if(~dp[i-1][j]){
MAX(dp[i][j],dp[i-1][j]);
MAX(dp[i][(j+stk.stk[i].first)%p],dp[i-1][j]+stk.stk[i].second);
}
}
void all_DP(Stack &stk,ll dp[M][505]){
memset(dp[stk.st],-1,sizeof(dp[stk.st]));
dp[stk.st][0]=0;
for(int i=stk.st+1;i<=stk.top;i++)DP(stk,dp,i);
}
void rebuild(Stack &stk1,Stack &stk2){
int mid=(stk1.st+stk1.top+1)/2;
for(int i=mid;i>stk1.st;i--)stk2.insert(stk1.stk[i]);
stk1.st=mid;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
memset(dp1,-1,sizeof(dp1));
memset(dp2,-1,sizeof(dp2));
rd(casid),rd(m),rd(p);
dp1[0][0]=dp2[0][0]=0;
for(int k=1;k<=m;k++){
char str[15];
int a,b;
scanf("%s",str);
if(0);
else if(str[0]=='I'&&str[1]=='F'){
rd(a),rd(b);
stk1.insert(make_pair(a,b));
DP(stk1,dp1,stk1.top);
}
else if(str[0]=='I'&&str[1]=='G'){
rd(a),rd(b);
stk2.insert(make_pair(a,b));
DP(stk2,dp2,stk2.top);
}
else if(str[0]=='D'&&str[1]=='F'){
if(stk1.empty())rebuild(stk2,stk1),all_DP(stk1,dp1),all_DP(stk2,dp2);
stk1.pop();
}
else if(str[0]=='D'&&str[1]=='G'){
if(stk2.empty())rebuild(stk1,stk2),all_DP(stk1,dp1),all_DP(stk2,dp2);
stk2.pop();
}
else if(str[0]=='Q'&&str[1]=='U'){
rd(a),rd(b);
for(int i=0;i<p;i++)A[i]=dp1[stk1.top][i];
for(int i=0;i<p;i++)B[i]=dp2[stk2.top][i];
int L=1,R=0;
ll ans=-1;
for(int i=a;i<=b;i++){
while(L<=R&&A[i]>A[Q[R]])R--;
Q[++R]=i;
}
for(int i=p-1;i>=0;i--){
while(L<=R&&A[(p+b-i)%p]>=A[Q[R]%p])R--;
Q[++R]=p+b-i;
while(L<=R&&Q[L]<p+a-i)L++;
if(L<=R&&~A[Q[L]%p]&&~B[i])MAX(ans,A[Q[L]%p]+B[i]);
}
printf("%lld\n",ans);
}
}
return (0-0);
}


## 哈希表实现map

unordered_map<int,int>mp;


struct hash_map{
static const int SIZE=1e6+5;
int Hash(int x){
if(x<0)x=-x;
return x%(SIZE-1)+1;
}
int& operator [](int x){
int pos=Hash(x);
if(key[i]==x)return value[i];
return value[tot];
}
hash_map(){clear();}
};


template<class KEY,class VALUE>struct hash_map{
static const int SIZE=1e6+5;
KEY key[SIZE*1];
VALUE value[SIZE*1];
int Hash(KEY x){
if(x<0)x=-x;
return x%(SIZE-1)+1;
}
VALUE& operator [](KEY x){
int pos=Hash(x);
if(key[i]==x)return value[i];
return value[tot];
}
hash_map(){clear();}
};


## 支持任意删除的大顶堆

struct Multiset{
priority_queue<int>A,B;
void clear(){
while(!A.empty())A.pop();
while(!B.empty())B.pop();
}
bool empty(){
return A.empty();
}
int top(){
if(A.empty())return -1e9;
else return A.top();
}
void pop(){
if(A.empty())return;
A.pop();
while(!A.empty()&&!B.empty()&&A.top()==B.top())A.pop(),B.pop();
}
void push(int x){
A.push(x);
}
void erase(int x){
B.push(x);
while(!A.empty()&&!B.empty()&&A.top()==B.top())A.pop(),B.pop();
}
};


# 树论

## 树的直径

const int SIZE=1e6+5;
inline void add_edge(int a,int b,int c){
to[++tot]=b;
co[tot]=c;
}
int mxID;
ll mxdis;
void dfs(int x,int f,ll d){
if(d>mxdis)mxdis=d,mxID=x;
int y=to[i];
if(y==f)continue;
dfs(y,x,d+co[i]);
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
rd(n);
for(int i=1;i<n;i++){
int a,b,c;
rd(a),rd(b),rd(c);
}
int s=0,t=0;
mxdis=-1,dfs(1,0,0),s=mxID;
mxdis=-1,dfs(s,0,0),t=mxID;
printf("%d %d %lld\n",s,t,mxdis);
return (0-0);
}


## 树的重心

1. 删除重心后所得的所有子树，节点数不超过原树的1/2，一棵树最多有两个重心，且它们相邻
2. 树中所有节点到重心的距离之和最小，如果有两个重心，那么它们距离之和相等
3. 两个树通过一条边合并，新的重心在原树两个重心的路径上
4. 树删除或添加一个叶子节点，重心最多只移动一条边
const int SIZE=1e6+5;
to[++tot]=b;
}
int sz[SIZE],miID,mival;
void dfs(int x,int f){
sz[x]=1;
int res=0;
int y=to[i];
if(y==f)continue;
dfs(y,x);
sz[x]+=sz[y];
MAX(res,sz[y]);
}
MAX(res,n-sz[x]);
if(res<mival)mival=res,miID=x;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
#endif
rd(n);
for(int i=1;i<n;i++){
int a,b;
rd(a),rd(b);
}
mival=1e9,miID=-1;
dfs(1,0);
printf("%d %d\n",miID,mival);
return (0-0);
}


## 树上倍增+LCA

const int SIZE=5e5+5;
int fa[SIZE][20],deep[SIZE];
to[++tot]=b;
}
void dfs(int x,int f){
deep[x]=deep[f]+1;
fa[x][0]=f;
for(int i=1;i<20;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
int y=to[i];
if(y==f)continue;
dfs(y,x);
}
}
int LCA(int x,int y){
if(deep[x]<deep[y])swap(x,y);
int step=deep[x]-deep[y];
for(int i=0;i<20;i++)if(step&1<<i)x=fa[x][i];
if(x==y)return x;
for(int i=19;i>=0;i--)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][0];
}


## 树链剖分+LCA

const int SIZE=5e5+5;
int dfsid,fa[SIZE],deep[SIZE],sz[SIZE],son[SIZE],top[SIZE],dfn[SIZE],reid[SIZE];
to[++tot]=b;
}
void dfs(int x,int f){
deep[x]=deep[f]+1;
fa[x]=f;
sz[x]=1;
son[x]=0;
int y=to[i];
if(y==f)continue;
dfs(y,x);
sz[x]+=sz[y];
if(sz[y]>sz[son[x]])son[x]=y;
}
}
void chain(int x,int k){
top[x]=k;
reid[dfn[x]=++dfsid]=x;
if(son[x])chain(son[x],k);
int y=to[i];
if(y==fa[x]||y==son[x])continue;
chain(y,y);
}
}
int LCA(int a,int b){
while(top[a]!=top[b]){
if(deep[top[a]]<deep[top[b]])swap(a,b);
a=fa[top[a]];
}
return deep[a]<deep[b]?a:b;
}


## RMQ+LCA

O(nlogn) 预处理 O(1) 求LCA

const int SIZE=5e5+5;
int dfsid,deep[SIZE],dfn[SIZE],st[SIZE<<1][20];
to[++tot]=b;
}
void dfs(int x,int f){
deep[x]=deep[f]+1;
dfn[x]=++dfsid;
st[dfsid][0]=x;
int y=to[i];
if(y==f)continue;
dfs(y,x);
st[++dfsid][0]=x;
}
}
void RMQ(){
for(int i=dfsid;i>=1;i--)for(int j=1;i+(1<<j)-1<=dfsid;j++)
st[i][j]=deep[st[i][j-1]]<deep[st[i+(1<<j-1)][j-1]]?st[i][j-1]:st[i+(1<<j-1)][j-1];
}
int LCA(int x,int y){
int l=dfn[x],r=dfn[y];
if(l>r)swap(l,r);
int k=log2(r-l+1);
return deep[st[l][k]]<deep[st[r-(1<<k)+1][k]]?st[l][k]:st[r-(1<<k)+1][k];
}


## 点分治

P3806

n个节点的边权树，m次询问，每次询问树上距离为k的点对是否存在

1 ≤ n ≤ 1 0 4 , 1 ≤ m ≤ 100 , 1 ≤ k ≤ 1 0 7 , 边 权 1 ≤ w ≤ 1 0 4 1≤n≤10^4,1≤m≤100,1≤k≤10^7,边权1≤w≤10^4

const int M=10005;
const int K=1e7+5;
inline void add_edge(int a,int b,int c){
to[++tot]=b;
co[tot]=c;
}
int n,m,Q[M],ans[M],cnt[K];
int all,used[M],tmp[M],sz[M],mx[M];
void center(int x,int f){
sz[x]=1;
mx[x]=0;
tmp[++all]=x;
for(int i=head[x