2018年模板大集合!!!!没有一个优秀的模板就是等着被摩擦

离散化模板:

//把需要离散化的内容进行排序,去重,然后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;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值