离散化模板:
//把需要离散化的内容进行排序,去重,然后getid为取得这个值的下标
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end()); //这里记得需要排序才可以去重
int getid(int x){
return lower_bound(v.begin(),v.end(),x)-v.begin()+1; //错1次,返回下标需要-v.begin()+1
}
判断vector中某个数字的个数:
(upper_bound(qq4.begin(),qq4.end(),c1[i])-qq4.begin())-
(lower_bound(qq4.begin(),qq4.end(),c1[i])-qq4.begin());
链式前向星:
struct E{
int next,to;
}e[maxn+maxn]; //链式前向星要开双倍的大小
void add(int u,int v){ //如果重复使用n+1,n+2个点建立边,记得把head[n+1]=-1
tot++;
e[tot].to=v;
e[tot].next=head[u];
head[u]=tot;
}
set使用结构体内排序,快速查找问题:
struct ttt{
int key,num;
bool operator < (const ttt &b) const{
return key<b.key;
}
};
set<ttt>G;
set<ttt>::iterator ip;
ip=G.upper_bound(u); //找到里面大于的第一个指针
树上跑st表,找到这个节点上的第k个点
ff[u][0]=pre; //pre为父结点,在dfs的时候预处理
for (int i=1;i<=20;i++)ff[u][i]=ff[ff[u][i-1]][i-1];
int find1(int u,int x){ //在这里找到u上面距离为x的点
if(x==0)return u;
for(int i=0;i<=20;i++)if(x&(1<<i))u=ff[u][i];
return u;
}
三种树状数组(每次操作复杂度均为logn):
1、区间求值,单点修改(基本用法。但是这里可用用作权值树状数组,可以用来查比a[i]大但是下标在i前面的数量
ll lowbits(ll x){
return x&(-x);
}
ll NN;
ll c1[maxn];//注意这里空间可能要开大一些
ll sum(ll x){
ll sum1=0;
while(x>0){
sum1+=c1[x];
x-=lowbits(x);
}
return sum1;
}
ll add(ll x,ll y){
while(x<=NN){
c1[x]+=y;
x+=lowbits(x);
}
}
2、单点求值,区间修改(区间加
ll l=3;
ll r=7,k=10;
add(l,k);
add(r+k,-k);//表示对区间[l,r]+k,查询某个点只需要查询sum(i)即为答案
3、区间求值,区间修改(区间加
int NN,c1[maxn],c2[maxn];
int lowbits(int x){ return x&(-x);}
void add(int *r,int pos,int v){
while(pos<=NN){
r[pos]+=v;
pos+=lowbits(pos);
}
}
int sigma(int *r,int pos){
int anx=0;
while(pos>0){
anx+=r[pos];
pos-=lowbits(pos);
}
return anx;
}
c1与c2均为数组传入,对[f1,f2]+f3
add(c1,f1,f3);add(c1,f2+1,-f3);
add(c2,f1,f3*(f1-1));add(c2,f2+1,-f3*f2);
查询[f1,f2]
sum1=(f1-1)*sigma(c1,f1-1)-sigma(c2,f1-1);
sum2=f2*sigma(c1,f2)-sigma(c2,f2);
cout << sum2-sum1 << endl;
并查集:
先init初始化,find1为寻找其的head,find2为并
int find1(int x){
int r=x;
while(r!=pre[r]){
r=pre[r];
}
int i=x,j;
while(pre[i]!=r){
j=pre[i];
pre[i]=r;
i=j;
}
return r;
}
int find2(int x,int y){
int x1=find1(x);
int y1=find1(y);
if(pre[x1]!=pre[y1]){
pre[x1]=pre[y1];
}
}
二分图匹配:
记得在for循环中,赋值vis全部为0,从左边点找右边点,但是算谁连接谁点时候应该从右边点点向前找pre
int bfs(int x){//最后pre[i]=x,表示i被X连接
for(int i=0;i<out[x].size();i++){
int v=out[x][i];
if(vis[v]==0){ //这里每次在循环中都需要清空一次
vis[v]=1;
if(pre[v]==-1||bfs(pre[v])){
pre[v]=x; //X之前连接点为out[x][i]
return 1;//这里点目的是求出最大匹配的数量
}
}
}
return 0;
}
通过逆元求组合数:
int inv(int a)
{
//return fpow(a, MOD-2, MOD);
return a==1?1:(long long)(MOD-MOD/a)*inv(MOD%a)%MOD;
}
LL C(LL n,LL m)
{
if(m<0)return 0;
if(n<m)return 0;
if(m>n-m)m=n-m;
LL up=1,down=1;
for(LL i=0;i<m;i++)
{
up=up*(n-i)%MOD;
down=down*(i+1)%MOD;
}
return up*inv(down)%MOD;
}
二进制压缩的背包DP:
for(i=1;i<=n;i++){
for(j=0;j<q1[i].num;j++){ //因为是2^k-1个,所以取的时候是1/2/4/8/2^(k-1)刚好全部覆盖
for(k=10000;k>=q1[i].v;k--){
if(k-(1<<j)*q1[i].v<0)break;
res[k]+=res[k-(1<<j)*q1[i].v];
res[k]%=mod;
}
}
}
矩阵快速幂板子:
const int N = 9;
struct Mat{
ll m[N][N];
void clear(){memset(m,0,sizeof(m));}
void I(){for (int i=0;i<N;i++) m[i][i]=1;}
};
Mat mul(const Mat &a, const Mat &b){
Mat c;c.clear();
for (int i=0;i<N;i++)
for (int j=0;j<N;j++){
for (int k=0;k<N;k++)
c.m[i][j]+=a.m[i][k]*b.m[k][j]%mod;
c.m[i][j]%=mod;
}
return c;
}
Mat qpow(Mat a, ll x){ //这里注意传进来的参数要为long long 不然会报超时
Mat ans; ans.clear(); ans.I();
for (;x;a=mul(a,a),x>>=1)
if (x&1)
ans=mul(ans,a);
return ans;
}
Mat t2=(Mat){{
{0,0},
{1,0}
}}; //初始化一个矩阵可以这么写出来
读入挂:
inline int read(){
int f=1,x=0;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
return f*x;
}
树剖的方法求LCA:
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+7;
struct node{
int to,next;
};
node edge[maxn+maxn]; //链式前向星开双倍
int cnt,head[maxn],pos;//注意初始化cnt和head为-1
void add(int x,int y){
cnt++;
edge[cnt].to=y;
edge[cnt].next=head[x];
head[x]=cnt;
}
int son[maxn],deep[maxn],fa[maxn],num[maxn];
void init(){
pos=0;
memset(son,-1,sizeof(son));//点的编号从1开始
}
void dfs(int u,int pre,int w){
deep[u]=w;//层数
num[u]=1;//子节点数量
fa[u]=pre;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(v==pre)continue;
dfs(v,u,w+1);
num[u]+=num[v];
if(son[u]==-1||num[son[u]]<num[v])
son[u]=v;
}
}
int p[maxn],fp[maxn],tp[maxn];
//p表示这个点的位置在哪(线段树中的下标)
//fp表示这个位置指哪一个点建树的时候就这么建
//tp表示v在哪个重链上
void dfs2(int u,int sd){
p[u]=++pos;fp[pos]=u;tp[u]=sd;
if(son[u]==-1)return ;
dfs2(son[u],sd);
for(int i=head[u];i!=-1;i=edge[i].next){ //注意head[u]
int v=edge[i].to;
if(v==son[u]||v==fa[u])continue;
dfs2(v,v); //此时v为新链
}
}
int LCA(int u,int v){
int uu=tp[u],vv=tp[v];
while(uu!=vv){
if(deep[vv]>deep[uu]){
swap(uu,vv);swap(u,v);
}
//从左边往右边应该是p[uu],p[u]
u=fa[u];uu=tp[u];
}
if(deep[u]>deep[v])swap(u,v);
//p[u],p[v] //u为根节点
return u;
}
int main(){
int i,j,k,f1,f2,f3,f4,f5,t1,t2,t3,t4,t5;
//freopen("in.txt","r",stdin);
int n,m,Q,S;
scanf("%d %d %d",&n,&Q,&S);
cnt=0;
memset(head,-1,sizeof(head));
init();
for(i=1;i<n;i++){
scanf("%d %d",&t1,&t2);
add(t1,t2);
add(t2,t1);
}
dfs(S,-1,1);
dfs2(S,S);
for(i=1;i<=Q;i++){
scanf("%d %d",&t1,&t2);
printf("%d\n",LCA(t1,t2));
}
return 0;
}
虚树模板,切割点集最小代价:
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+7;
typedef long long ll;
struct node{
int to,next,w;
};
node edge[maxn+maxn]; //链式前向星开双倍
int cnt,head[maxn];//注意初始化cnt和head为-1
void add(int x,int y,int w){
cnt++;
edge[cnt].to=y;
edge[cnt].w=w;
edge[cnt].next=head[x];
head[x]=cnt;
}
vector<int>v[maxn];
int pos;
int son[maxn],deep[maxn],fa[maxn],num[maxn];
//son表示这个点的重儿子是哪个点,deep表示这个点的深度是多少,fa就是这个点的父结点是哪个,num是指这个点的子节点有几个
void init(){ //初始化pos表示新数组的结点编号,初始化全部重儿子为-1
pos=0; //点的编号从1开始
memset(son,-1,sizeof(son));
}
ll mn[maxn];//存从根到这个点最小的值
void dfs(int u,int pre,int w){
deep[u]=w;//层数
num[u]=1;//子节点数量
fa[u]=pre;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(v==pre)continue;
mn[v]=min(mn[u],(ll)edge[i].w);
dfs(v,u,w+1);
num[u]+=num[v];
if(son[u]==-1||num[son[u]]<num[v])
son[u]=v;
}
}
int p[maxn],fp[maxn],tp[maxn];
//p表示这个点的位置在哪(在新数组中)
//fp表示这个位置指的是哪个点
//tp表示v点在哪个重链上
void dfs2(int u,int sd){ //fp是从重链开始从下标1开始到n的,建树也是根据链来建立
p[u]=++pos;fp[pos]=u;tp[u]=sd;
if(son[u]==-1)return ;
dfs2(son[u],sd); //接着对这个u结点的重儿子进行剖分,都是在链sd上
for(int i=head[u];i!=-1;i=edge[i].next){ //这里下标错了很多次
int v=edge[i].to;//表示v这个点
if(v==son[u]||v==fa[u])continue; //这个点的重儿子和父亲跳过
dfs2(v,v);
}
}
int LCA(int u,int v){
int uu=tp[u],vv=tp[v];
while(uu!=vv){
if(deep[vv]>deep[uu]){
swap(uu,vv);swap(u,v);
}
//从左边往右边应该是p[uu],p[u]
u=fa[u];uu=tp[u];
}
if(deep[u]>deep[v])swap(u,v);
//p[u],p[v] //u为根节点
return u;
}
int A[maxn];
int cmp1(int x,int y){
return p[x]<p[y];
}
int top;
int s[maxn]; //这里s为一个栈的模型
bool vis[maxn];
vector<int>vv;
void insert(int x){
if(!top){s[++top]=x;return ;}
int lca=LCA(x,s[top]);
while(top>1&&p[s[top-1]]>p[lca]){ //p与lca之间的那个点q,直接连向p点,如果有很多就全连了
v[s[top-1]].push_back(s[top]),top--;//这里构造的为单向
}
if(p[lca]<p[s[top]]){
vv.push_back(lca);
v[lca].push_back(s[top]);top--;
}
if(!top||p[lca]>p[s[top]])s[++top]=lca;
s[++top]=x;
}
ll DP(int x){
if(vis[x]&&x!=1)return mn[x];
ll sum=0;
for(int i=0;i<v[x].size();i++){
sum+=DP(v[x][i]);
}
return min(sum,(ll)mn[x]);
}
int main(){
int i,j,k,f1,f2,f3,f4,f5,t1,t2,t3,t4,t5;
int n,m;
//freopen("in.txt","r",stdin);
int T;
while(scanf("%d",&n)==1){
cnt =0;
init();
memset(edge,0,sizeof(edge));
memset(head,-1,sizeof(head));
for(i=1;i<=n;i++){
mn[i]=1e13+7;
}
for(i=1;i<n;i++){
scanf("%d %d %d",&t1,&t2,&t3);
add(t1,t2,t3);
add(t2,t1,t3);
}
dfs(1,0,1);
dfs2(1,1);
scanf("%d",&m);
int K;
for(i=1;i<=m;i++){
scanf("%d",&K);
for(j=1;j<=K;j++){
scanf("%d",&A[j]);
vis[A[j]]=1;
vv.push_back(A[j]);
}
vis[1]=1;vv.push_back(1);K++;A[K]=1;
sort(A+1,A+1+K,cmp1);
top=0;
for(j=1;j<=K;j++)insert(A[j]); //插入一个ai位置,注意下标
while(top > 1){ //然后在把栈中的全部拿出来建树
v[s[top - 1]].push_back(s[top]), top--;
}
printf("%lld\n",DP(1));
for(j=0;j<vv.size();j++){
v[vv[j]].clear();
vis[vv[j]]=0;
}
vv.clear();
}
}
return 0;
}
空间换时间,相当于比较快一点的map:
#include <iostream>
using namespace std;
const int mod=1007;
int head[500],next[500],st[500],n;
int insert(int s){
int h=n%mod;
int u=head[h];
while(u){
if(st[u]==st[s])return 0;
u=next[u];
}
next[s]=head[h];
head[h]=s;
return 1;
}
int main(){
int g=1;
while(cin >> n){
st[g]=n;
if(insert(g)){
g++;
cout << "插入成功" << endl;
}else{
cout << "插入失败" << endl;
}
}
}
Splay模板(对值进行增删、前驱、后继、排名为x、x的排名):
注意找的时候是严格大于,如果前驱可以找自己,注意特判一下
#include<bits/stdc++.h>
using namespace std;
#define MAXN 100010
struct Node{
int key, sz, cnt; //cnt表示结点值为key的结点的数量
Node *ch[2], *pnt;//左右儿子和父亲
Node(){} //给Node函数赋值。
Node(int x, int y, int z){key = x, sz = y, cnt = z;}
void rs(){//这个函数的含义就是旋转后,这2个结点的大小已经发生了改变
sz = ch[0]->sz + ch[1]->sz + cnt;
}
}nil(0, 0, 0), *NIL = &nil;
Node nod[MAXN];
int ncnt;//计算key值不同的结点数,注意已经去重了*
struct Splay{//伸展树结构体类型
Node *root;
int nncnt;//作为一个splay的大小
void init(){// 首先要初始化*
root = NIL;
nncnt = 0;
}
void rotate(Node *x, bool d){//旋转操作,d为true表示右旋
Node *y = x->pnt;
y->ch[!d] = x->ch[d];//x右旋的时候,y的左儿子为x的右边儿
if (x->ch[d] != NIL)//如果x的右儿子不为空那么这个右儿子的父亲是y
x->ch[d]->pnt = y;
x->pnt = y->pnt;//把y的父亲赋值给x的父亲,相当x替代了y的位子
if (y->pnt != NIL){//把y的父亲的左儿子或右儿子从y改为x
if (y == y->pnt->ch[d])
y->pnt->ch[d] = x;
else
y->pnt->ch[!d] = x;
}
x->ch[d] = y;
y->pnt = x;
y->rs();
x->rs();
}
void splay(Node *x, Node *target){//将x伸展到target的儿子位置处
Node *y;
while (x->pnt != target){ //当target时,就转x为根结点
y = x->pnt;
if (x == y->ch[0]){
if (y->pnt != target && y == y->pnt->ch[0])//如果x是y的左结点,且y的父亲结点不是目标结点
rotate(y, true);//并且y是y的左结点才旋转y否则旋转x
rotate(x, true);
}
else{
if (y->pnt != target && y == y->pnt->ch[1])
rotate(y, false);
rotate(x, false);
}
}
if (target == NIL)
root = x;
}
void debug(Node *u,int deep=0){
if(u == NIL) return ;
debug(u->ch[1],deep+1);
for(int i=0;i<deep;i++)cout <<" ";
cout << u->key <<endl;
debug(u->ch[0],deep+1);
if(deep == 0) cout <<endl;
}
/************************以上一般不用修改************************/
void insert(int key){//插入一个值
if (root == NIL){//初始化根节点
nncnt = 0; //用来记录每颗树的结点数字
root = &nod[++ncnt];nncnt++; //nod 为总结点数
root->ch[0] = root->ch[1] = root->pnt = NIL; //左右结点和父亲结点
root->key = key; //这里表示这个结点的值
root->sz = root->cnt = 1;//sz表示这个结点作为根的大小
return;
}
Node *x = root, *y;
int mark;
while (1){ //从x开始向下找,刚开始x就是root
x->sz++; //这里表示sz的结点数
if (key == x->key){
x->cnt++;
x->rs();
y = x;break;
}else if (key < x->key) mark=0;//这里表示如果新加入的值比x小,且左结点为空,那么就加入其作为左儿子
else mark=1;
if (x->ch[mark] != NIL)
x = x->ch[mark];
else{
x->ch[mark] = &nod[++ncnt];nncnt++;
y = x->ch[mark];
y->key = key;
y->sz = y->cnt = 1;
y->ch[0] = y->ch[1] = NIL;
y->pnt = x;
break;
}
}
splay(y, NIL); //相当于把这个新加入点点移动到root来
}
Node* search(int key){ //查找一个值并且把这个值旋上来,返回指针,也就是这个点
if (root == NIL)
return NIL;
Node *x = root, *y = NIL;
int mark;
while (1){
if (key == x->key){
y = x;
break;
}else if(key > x->key)mark=1;
else mark=0;
if (x->ch[mark] != NIL) //这里用来判断左儿子还是右儿子
x = x->ch[mark];
else
break;
}
splay(x, NIL);
return y;
}
Node* searchmin(Node *x){//查找最小值,返回指针
Node *y = x->pnt;
while (x->ch[0] != NIL){//遍历到最左的儿子就是最小值
x = x->ch[0];}
splay(x, y);
return x;
}
void del(int key){//删除一个值
if (root == NIL)return;
Node *x = search(key), *y;
if (x == NIL)return;
if (x->cnt > 1){ //如果这个结点还有,那么它就变成根结点
x->cnt--; //个数变小
x->rs();
return;
}else if (x->ch[0] == NIL && x->ch[1] == NIL){ //这个是最后一个结点
init();return;
}else if (x->ch[0] == NIL){ //删除点为根,找一个结点做根
root = x->ch[1];
x->ch[1]->pnt = NIL;
return;
}else if (x->ch[1] == NIL){
root = x->ch[0];
x->ch[0]->pnt = NIL;
return;
}
y = searchmin(x->ch[1]);
y->pnt = NIL;
y->ch[0] = x->ch[0];
x->ch[0]->pnt = y;
y->rs();
root = y;
}
int rank(int key){//查询一个值的排名是多少
//求第几个位置的数字
Node *x = search(key);
if (x == NIL) //如果找不到这个值
return 0;
return x->ch[0]->sz + 1;/* or x->cnt*/
}
Node* findk(int kth){//查找第k小的值,这里是值,可以有重复 如1,1,2,找3是2
if (root == NIL || kth > root->sz)
return NIL;
Node *x = root;
while (1){
if (x->ch[0]->sz +1 <= kth && kth <= x->ch[0]->sz + x->cnt)
break;
else if (kth <= x->ch[0]->sz)
x = x->ch[0];
else{
kth -= x->ch[0]->sz + x->cnt;
x = x->ch[1];
}
}
splay(x, NIL);
return x;
}
Node* pre() {//求x的前驱 前驱定义为小于x,且最大的数
Node *now=root->ch[0];//求x的前驱其实就是求x的左子树的最右边的一个结点
while(now->ch[1]!=NIL) now=now->ch[1];
return now;
}
Node* next() {//先插入要找的数,求x的后继 后继定义为大于x,且最小的数
Node *now=root->ch[1];//后继是求x的右子树的左边一个结点
while(now->ch[0]!=NIL) now=now->ch[0];
return now;
}
}sp;
char s1[15];
int main(){ //每次sp的返回都是一个指针
//freopen("in.txt","r",stdin);
sp.init();
int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4,n,m;
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d %d",&t1,&t2);
if(t1==1){ //插入
sp.insert(t2);
}else if(t1==2){//删除
sp.del(t2);
}else if(t1==3){//排第t2的数的值,比如3,对于1、1、2、2、结果是2
cout << sp.rank(t2) << endl;
}else if(t1==4){ //找第k个数字
Node *x=sp.findk(t2);
cout << (x->key) << endl;
}else if(t1==5){
sp.insert(t2);
Node *x =sp.pre();
cout << x->key<< endl;
sp.del(t2);
}else if(t1==6){
sp.insert(t2);
Node *x =sp.next();
cout << x->key<< endl;
sp.del(t2);
}
}
return 0;
}
序列Splay(区间改+区间最值+翻转):
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
typedef long long ll;
#define LS(n) node[(n)].ch[0]
#define RS(n) node[(n)].ch[1]
const int INF = 2e9+7;
struct Node{
int fa,ch[2]; //父亲与两个子节点
bool rev;//标记是否翻转
int val,add,Max,size;//值,增加的标记,最大值和节点个数
void init(int _val){val = Max = _val;size=1;add=rev=ch[1]=ch[0]=0;}
}node[maxn];
struct Splay{
int root;
void rotate(int n, bool kind) {
int fn = node[n].fa;
int ffn = node[fn].fa;
node[fn].ch[!kind] = node[n].ch[kind];
node[node[n].ch[kind]].fa = fn;
node[n].ch[kind] = fn;
node[fn].fa = n;
node[ffn].ch[RS(ffn) == fn] = n;
node[n].fa = ffn;
up(fn);
}
void splay(int n, int goal) {
while(node[n].fa != goal) {
int fn = node[n].fa;
int ffn = node[fn].fa;
down(ffn); down(fn); down(n);
bool rotate_n = (LS(fn) == n);
bool rotate_fn = (LS(ffn) == fn);
if(ffn == goal) rotate(n, rotate_n);
else {
if(rotate_n == rotate_fn) rotate(fn, rotate_fn);
else rotate(n, rotate_n);
rotate(n, rotate_fn);
}
}
up(n);
if(goal == 0) root = n;
}
void up(int n){
node[n].Max = max(node[n].val, max(node[LS(n)].Max, node[RS(n)].Max));
node[n].size = node[LS(n)].size + node[RS(n)].size + 1;
}
void down(int n){
if(n == 0) return ;
if(node[n].add) {
if(LS(n)) {
node[LS(n)].val += node[n].add;
node[LS(n)].Max += node[n].add;
node[LS(n)].add += node[n].add;
}
if(RS(n)) {
node[RS(n)].val += node[n].add;
node[RS(n)].Max += node[n].add;
node[RS(n)].add += node[n].add;
}
node[n].add = 0;
}
if(node[n].rev) {
if(LS(n)) node[LS(n)].rev ^= 1;
if(RS(n)) node[RS(n)].rev ^= 1;
swap(LS(n), RS(n));
node[n].rev = 0;
}
}
int select(int pos) { //对序列来说,寻找第pos个点,并提上来
int u = root;
down(u);
while(node[LS(u)].size != pos) {
if(pos < node[LS(u)].size)
u = LS(u);
else {
pos -= node[LS(u)].size + 1;
u = RS(u);
}
down(u);
}
up(u);
splay(u,0);
return u;
}
int queryMax(int L, int R) { //查询区间最大值
int u = select(L - 1), v = select(R + 1);
splay(u, 0); splay(v, u); //通过旋转操作把询问的区间聚集到根的右子树的左子树下
return node[LS(v)].Max;
}
void add(int L, int R, int val) { //区间更新累加val
int u = select(L - 1), v = select(R + 1);
splay(u, 0); splay(v, u);
node[LS(v)].val += val;
node[LS(v)].Max += val;
node[LS(v)].add += val;
}
void reverse(int L, int R) { //区间翻转
int u = select(L - 1), v = select(R + 1);
splay(u, 0); splay(v, u);
node[LS(v)].rev ^= 1;
}
int build(int L, int R) { //初始建树
if(L > R) return 0;
if(L == R) return L;
int mid = (L + R) >> 1;
int r_L, r_R;
LS(mid) = r_L = build(L, mid - 1);
RS(mid) = r_R = build(mid + 1, R);
node[r_L].fa = node[r_R].fa = mid;
up(mid);
return mid;
}
void init(int n) { //这个init仅仅针对这道题
node[0].init(-INF); node[0].size = 0;node[1].init(-INF); //s
node[n + 2].init(-INF); //因为求最大值 那么这里设无限小
for(int i = 2; i <= n + 1; ++i)
node[i].init(0);
root = build(1, n + 2);
node[root].fa = 0;
node[0].fa = 0;
LS(0) = root; //0的做儿子是root ??
}
};
Splay sp;
int main(){
int n, m;
//freopen("in.txt","r",stdin);
scanf("%d%d", &n, &m);
sp.init(n);
for(int i = 0; i < m; ++i) {
int op, l, r, v;
scanf("%d", &op);
if(op == 1) {
scanf("%d%d%d", &l, &r, &v);
sp.add(l, r, v);
} else if(op == 2) {
scanf("%d%d", &l, &r);
sp.reverse(l, r);
} else {
scanf("%d%d", &l, &r);
printf("%d\n",sp.queryMax(l, r));
}
}
return 0;
}