zoukankan      html  css  js  c++  java
  • 模板整理——数据结构部分

    - 数据结构

    - 并查集

    • 路径压缩实现((mathcal O(nalpha (n)))
    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 10005
    
    int fa[MAXN];
    int n,m,t,p,q;
    
    void init(int n){for(int i=1;i<=n;i++) fa[i]=i;}
    int getf(int u){return fa[u]=(fa[u]==u)?u:getf(fa[u]);}
    void merge(int u,int v){int t1=getf(u),t2=getf(v);if(t1^t2) fa[t2]=t1;}
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	init(n);//不要忘 
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%d%d%d",&t,&p,&q);
    		if(t==1) merge(p,q);
    		else
    		{
    			p=getf(p),q=getf(q);
    			(p==q)?puts("Y"):puts("N");
    		}
    	} 
    	return 0;
    }
    
    • 按秩合并实现((mathcal O(nlog n))
    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 10005
    
    int fa[MAXN],si[MAXN];
    int n,m,t,p,q;
    
    void init(int n){for(int i=1;i<=n;i++) fa[i]=i,si[i]=1;}
    int getf(int u){return (fa[u]==u)?u:getf(fa[u]);}
    void merge(int u,int v)
    {
    	int t1=getf(u),t2=getf(v);
    	if(t1==t2) return;
    	if(si[t1]<si[t2]) swap(t1,t2);
    	fa[t2]=fa[t1],si[t1]+=si[t2];
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	init(n);//不要忘 
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%d%d%d",&t,&p,&q);
    		if(t==1) merge(p,q);
    		else
    		{
    			p=getf(p),q=getf(q);
    			(p==q)?puts("Y"):puts("N");
    		}
    	} 
    	return 0;
    }
    

    - 单调栈(mathcal O(n))

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define MAXN 3000005
    
    int n,f[MAXN],x;
    int loc[MAXN],st[MAXN],top=0;
    
    int main()
    {
        read(n);
        for(int i=1;i<=n;i++)
        {
            read(x);
            while(x>st[top]&&top) f[loc[top--]]=i;
            st[++top]=x,loc[top]=i;
        }
        for(int i=1;i<=n;i++) printf("%d ",f[i]);
        return puts(""),0;
    }
    

    - 单调队列(滑动窗口问题)(mathcal O(n))

    #include"iostream"
    #include"cstring"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 1000005
    
    int n,k;
    int a[MAXN],q[MAXN],loc[MAXN];
    int head,tail;
    
    void getmin()
    {
    	head=0,tail=1;
    	memset(q,0,sizeof(q)),memset(loc,0,sizeof(loc));
    	for(int i=1;i<=n;i++)
    	{
    		while(a[i]<=q[head]&&head>=tail) head--;
    		q[++head]=a[i],loc[head]=i;
    		if(loc[tail]<i-k+1) tail++;
    		if(i>=k) printf("%d ",q[tail]);
    	}
    	puts("");
    }
    
    void getmax()
    {
    	head=0,tail=1;
    	memset(q,0,sizeof(q)),memset(loc,0,sizeof(loc));
    	for(int i=1;i<=n;i++)
    	{
    		while(a[i]>=q[head]&&head>=tail) head--;
    		q[++head]=a[i],loc[head]=i;
    		if(loc[tail]<i-k+1) tail++;
    		if(i>=k) printf("%d ",q[tail]);
    	}
    	puts("");
    }
    
    int main()
    {
    	scanf("%d%d",&n,&k);
    	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    	getmin(),getmax();
    	return 0;
    }
    

    - ST 表(静态区间最值)(mathcal O(nlog n))

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 100005
    
    int n,m;
    int st[MAXN][18];
    int a[MAXN];
    int l,r;
    int h;
    
    int logx(int x){return floor(log(x)/log(2));}
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++) scanf("%d",&a[i]),st[i][0]=a[i];
    	h=logx(n);
    	for(int i=1;i<=h;i++)
    	{
    		int rt=1<<i;
    		for(int l=1;l+rt-1<=n;l++)
    		{
    			st[l][i]=max(st[l][i-1],st[l+(rt>>1)][i-1]);
    		}
    	}
    	while(m--)
    	{
    		scanf("%d%d",&l,&r);
    		int rt=logx(r-l+1);
    		printf("%d
    ",max(st[l][rt],st[r-(1<<rt)+1][rt]));
    	}
    	return 0;
    }
    

    - 树状数组1(mathcal O(mlog n))

    - 树状数组2(mathcal O(mlog n))

    //这是树状数组1的代码
    #include"iostream"
    #include"cstring"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 500005
    
    int n,m;
    int v,x,y;
    int t[MAXN],a[MAXN];
    
    int lowbit(int x){return x&(-x);}
    void add(int x,int y){for(int i=x;i<=n;i+=lowbit(i)) t[i]+=y;}
    int query(int x){int ans=0;for(int i=x;i>=1;i-=lowbit(i)) ans+=t[i];return ans;}
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++) scanf("%d",&a[i]),add(i,a[i]);
    	while(m--)
    	{
    		scanf("%d%d%d",&v,&x,&y);
    		if(v==1) add(x,y);
    		else printf("%d
    ",query(y)-query(x-1));
    	}
    	return 0;
    }
    
    • 利用树状数组实现区间加和区间求和

    仍然考虑使用差分数组 (p_i)

    考虑做前缀和:

    [sum_{i=1}^nsum_{j=1}^i p_j=sum_{i=1}^nsum_{j=1}^i p_i=sum_{i=1}^n(n-i+1) imes p_i=(n+1)sum_{i=1}^np_i-sum_{i=1}^nip_i ]

    两个树状数组分别维护即可。

    - 线段树1

    - 线段树2

    都是 (mathcal O(mlog n))

    //线段树1
    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define MAXN 100005
    #define ll long long
    
    struct node
    {
        ll lazy,sum;
    }a[MAXN<<2];
    int n,m;
    int v,l,r;
    ll t[MAXN],x;
    
    void update(int k){a[k].sum=a[k<<1].sum+a[k<<1|1].sum;}
    
    void build(int k,int l,int r)
    {
        if(l==r){a[k].sum=t[l];return;}
        int mid=(l+r)>>1;
        build(k<<1,l,mid),build(k<<1|1,mid+1,r);
        update(k);
    }
    
    void lazydown(int k,int l,int r)
    {
        if(l==r){a[k].lazy=0;return;}
        int mid=(l+r)>>1;
        a[k<<1].sum=a[k<<1].sum+(ll)(mid-l+1)*a[k].lazy;
        a[k<<1|1].sum=a[k<<1|1].sum+(ll)(r-mid)*a[k].lazy;
        a[k<<1].lazy+=a[k].lazy,a[k<<1|1].lazy+=a[k].lazy;
        a[k].lazy=0;
        return;
    }
    
    void modify(int k,int l,int r,int x,int y,ll op)
    {
    	lazydown(k,x,y);//这里千千万万不能忘啊! 
        if(x==l&&r==y)
        {
            a[k].sum=a[k].sum+(ll)(y-x+1)*op;
            a[k].lazy+=op;
            return;
        }
        int mid=(x+y)>>1;
        if(r<=mid) modify(k<<1,l,r,x,mid,op);
        else if(l>mid) modify(k<<1|1,l,r,mid+1,y,op);
        else modify(k<<1,l,mid,x,mid,op),modify(k<<1|1,mid+1,r,mid+1,y,op);
        update(k);
    }
    
    ll query(int k,int l,int r,int x,int y)
    {
        if(x==l&&r==y) return a[k].sum;
        lazydown(k,x,y);
        int mid=(x+y)>>1;
        if(r<=mid) return query(k<<1,l,r,x,mid);
        else if(l>mid) return query(k<<1|1,l,r,mid+1,y);
        else return query(k<<1,l,mid,x,mid)+query(k<<1|1,mid+1,r,mid+1,y);
    }
    
    int main()
    {
        read(n),read(m);
        for(int i=1;i<=n;i++) scanf("%lld",&t[i]);
        build(1,1,n);
        for(int i=1;i<=m;i++)
        {
            read(v),read(l),read(r);
            if(v==1) scanf("%lld",&x),modify(1,l,r,1,n,x);
            else printf("%lld
    ",query(1,l,r,1,n));
        }
        return 0;
    }
    

    其他操作:

    小白逛公园

    区间最大子段和,单点修改。

    无聊的数列

    区间加等差数列,单点查询。

    如果是区间求和就记录一下首项与公差,然后按等差数列性质向下更新。

    黑匣子

    权值线段树,为主席树开路。

    序列操作

    线段树问题多合一。

    冒泡排序

    线段树上二分。

    GSS4

    区间开根。(这是一种思想,如区间除定值大数,区间取 (log) 都可以用相似的实现方法)。

    理论复杂度是 (mathcal O( ext{开根次数} imes n+nlog n))

    CF446C

    区间加斐波那契数列。

    我们考虑到斐波那契数列递推式的可加性,两组合并为一组也是可以的。

    所以考虑维护一下新的数列的前两项,可以反映整个数列。

    • 莫队(mathcal O(msqrt{n}))

    别问我为啥挂这个题啊/jk。

    #include"cstdio"
    #include"cmath"
    #include"iostream"
    #include"algorithm"
    using namespace std;
    
    int n,m;
    int a[30005],ans[200005];
    struct node
    {
        int l,r,id,pos;
    }q[200005];
    int cnt[1000005];
    int now=0;
    
    bool cmp(node n,node m){if(n.pos==m.pos) return n.r<m.r;else return n.pos<m.pos;}
    
    int main()
    {
        scanf("%d",&n);
        int h=sqrt(n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        scanf("%d",&m);
        for(int i=1;i<=m;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].pos=q[i].l/h,q[i].id=i;
        sort(q+1,q+m+1,cmp);
        int l=1,r=0;
       for(int i=1;i<=m;i++)
        {
            while(l>q[i].l){if(!cnt[a[--l]]) now++;cnt[a[l]]++;}
            while(r<q[i].r){if(!cnt[a[++r]]) now++;cnt[a[r]]++;}
            while(l<q[i].l){if(cnt[a[l]]==1) now--;cnt[a[l++]]--;}
            while(r>q[i].r){if(cnt[a[r]]==1) now--;cnt[a[r--]]--;}
            ans[q[i].id]=now;
        }
        for(int i=1;i<=m;i++) printf("%d
    ",ans[i]);
        return 0;
    }
    

    - 可并堆(mathcal O(nlog n))

    • 配对堆
    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define read(x) scanf("%d",&x)
    #define MAXN 100005
    
    int n,m;
    int f[MAXN],val[MAXN];
    int t,x,y;
    struct node
    {
        int to,nxt;
    }e[MAXN<<1];//因为要重复加边,所以最好要开大一些(虽然不开也过了) 
    int head[MAXN],cnt=0;
    int le[MAXN];
    
    void add(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}
    
    int getf(int u){return f[u]=(f[u]==u)?u:getf(f[u]);}
    
    int merge(int u,int v)
    {
        int t1=getf(u),t2=getf(v);
        if(t1==t2) return 0;
        if(val[t1]>val[t2]||(val[t1]==val[t2]&&t1>t2)) swap(t1,t2);
        //维护优先级(数字大小,序号)
        f[t2]=t1,add(t1,t2);//加边
        return t1;
    }
    
    void del(int u)
    {
        int lst=0;
        for(int i=head[u];i;i=e[i].nxt)
        {
            int j=e[i].to;
            f[j]=j;
            if(!lst) lst=j;//更新现在的根
            else lst=merge(lst,j);
        }
        f[u]=lst;//此处十分重要,为了在找根的时候方便,将原来根的父亲设为现在的根,相当于修改了所有节点的根
        return;
    }
    
    int main()
    {
        read(n),read(m);
        for(int i=1;i<=n;i++) read(val[i]),f[i]=i,le[i]=1;
        //le[]数组用来维护是否存在
        for(int i=1;i<=m;i++)
        {
            read(t),read(x);
            if(t==1)
            {
                read(y);
                if(le[x]&&le[y]) merge(x,y);
            }
            else
            {
                int op=getf(x);
                if(!le[x]) printf("-1
    ");
                else
                {
                    printf("%d
    ",val[op]);
                    le[op]=0;
                    del(op);
                }
            }
        }
        return 0;
    }
    
    • 左偏树(这个我忘了,也不想学了,就贴一发远古代码吧)
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    int son[2][100005],fa[100005],dis[100005],w[100005];
    int n,m,flag,l,r;
    bool vis[100005]={false};
    
    void swap(int *a,int *b)
    {
    	int t=*a;
    	*a=*b;
    	*b=t;
    	return;
    }
    
    void init()
    {
    	for(int i=1;i<=n;i++) fa[i]=i;
    	return;
    }
    
    int getf(int u)
    {
    	return u==fa[u]?fa[u]:fa[u]=getf(fa[u]);
    }
    
    int merge (int x,int y)//x是主堆,y是待合并堆 
    {
    	if(!x||!y) return x|y;
    	//维护堆,搜索 
    	if(w[x]>w[y]||(w[x]==w[y]&&x>y)) swap(&x,&y);
    	//维护好了就合并
    	son[1][x]=merge(son[1][x],y);
    	//维护左偏性质
    	if(dis[son[0][x]]<dis[son[1][x]]) swap(&son[0][x],&son[1][x]);
    	//维护dis数组
    	dis[x]=dis[son[1][x]]+1;
    	//真正的树形结构,维护父亲,可路径压缩
    	fa[son[1][x]]=fa[son[0][x]]=x;
    	return x; 
    }
    
    //维护删点操作
    int delate(int x)
    {
    	if(vis[x]) return -1;
    	vis[x]=true;
    	//数据真坑 
    	//此点的左右儿子的父亲是他自己
    	int minn=w[x];
    	fa[son[1][x]]=son[1][x];
    	fa[son[0][x]]=son[0][x];
    	//合并两子树
    	fa[x]=merge(son[1][x],son[0][x]);
    	return 	minn;
    } 
    
    inline int read()
    {
    	int x=0,w=1;
    	char c=getchar();
    	while(c<'0'||c>'9')
    	{
    		if(c=='-') w=-1;
    		c=getchar();
    	}
    	while(c>='0'&&c<='9')
    	{
    		x=(x<<3)+(x<<1)+c-'0';
    		c=getchar(); 
    	} 
    	return x*w;
    }
    
    int main()
    {
    	n=read(),m=read();
    	init();
    	for(int i=1;i<=n;i++)
    	{
    		w[i]=read();
    	}
    	for(int i=1;i<=m;i++)
    	{
    		flag=read();
    		if(flag==1) 
    		{
    			l=read(),r=read();
    			if(!vis[l]&&!vis[r]) merge(getf(l),getf(r));
    		}
    		else 
    		{
    			l=read();
    			if(vis[l]) printf("-1
    "); 
    			else printf("%d
    ",delate(getf(l)));
    		}
    	}
    	return 0;
    }
    
    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 100005
    #define read(x) scanf("%d",&x)
    #define ll long long 
    
    int n,m;
    int u,v;
    int ty,x,y,z;
    int p,root;
    struct node
    {
        int to,nxt;
    }e[MAXN<<1];
    int head[MAXN],cnt=0;
    int tot[MAXN],f[MAXN],top[MAXN],dep[MAXN];
    int son[MAXN],id[MAXN],rt=0;
    struct Tree
    {
        int l,r;
        ll sum,lazy;
    }a[MAXN<<2];
    int b[MAXN],t[MAXN];
    
    void add(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}
    
    int dfs1(int cur,int fa)
    {
        int maxn=0;
        tot[cur]=1,f[cur]=fa,dep[cur]=dep[f[cur]]+1;
        for(int i=head[cur];i;i=e[i].nxt)
        {
            int j=e[i].to;
            if(j==fa) continue;
            int op=dfs1(j,cur);
            if(op>maxn) son[cur]=j,maxn=op;
            tot[cur]+=op;
        }
        return tot[cur];
    }
    
    void dfs2(int cur,int topf)
    {
        top[cur]=topf,id[cur]=++rt;
        if(son[cur]) dfs2(son[cur],topf);
        for(int i=head[cur];i;i=e[i].nxt)
        {
            int j=e[i].to;
            if(j==f[cur]||j==son[cur]) continue;
            dfs2(j,j);
        }
        return;
    }
    
    inline void update(int k){a[k].sum=a[k<<1].sum+a[k<<1|1].sum;}
    
    void build(int k,int l,int r)
    {
        a[k].l=l,a[k].r=r;
        if(l==r){a[k].sum=1ll*t[l];return;}
        int mid=(l+r)>>1;
        build(k<<1,l,mid),build(k<<1|1,mid+1,r);
        update(k);
    }
    
    void lazydown(int k)
    {
        if(a[k].l==a[k].r){a[k].lazy=0;return;}
        a[k<<1].sum=(a[k<<1].sum+1ll*(a[k<<1].r-a[k<<1].l+1)%p*a[k].lazy%p)%p;
        a[k<<1|1].sum=(a[k<<1|1].sum+1ll*(a[k<<1|1].r-a[k<<1|1].l+1)%p*a[k].lazy%p)%p;
        a[k<<1].lazy=(a[k<<1].lazy+a[k].lazy)%p;
        a[k<<1|1].lazy=(a[k<<1|1].lazy+a[k].lazy)%p;
        a[k].lazy=0;
    }
    
    void modify(int k,int l,int r,ll x)
    {
        if(a[k].l==l&&a[k].r==r)
        {
            a[k].sum=(a[k].sum+1ll*(a[k].r-a[k].l+1)%p*x%p)%p;
            a[k].lazy=(a[k].lazy+x)%p;
            return;
        }
        if(a[k].lazy) lazydown(k);
        int mid=a[k<<1].r;
        if(r<=mid) modify(k<<1,l,r,x);
        else if(l>mid) modify(k<<1|1,l,r,x);
        else modify(k<<1,l,mid,x),modify(k<<1|1,mid+1,r,x);
        update(k);
    }
    
    ll query(int k,int l,int r)
    {
        if(a[k].l==l&&a[k].r==r) return a[k].sum%p;
        if(a[k].lazy) lazydown(k);
        int mid=a[k<<1].r;
        if(r<=mid) return query(k<<1,l,r);
        else if(l>mid) return query(k<<1|1,l,r);
        else return (query(k<<1,l,mid)+query(k<<1|1,mid+1,r))%p;
    }
    
    void mod_chain(int l,int r,int x)
    {
    	while(top[l]!=top[r])
    	{
    		if(dep[top[l]]<dep[top[r]]) swap(l,r);
    		modify(1,id[top[l]],id[l],x);
    		l=f[top[l]];
    	}
    	if(dep[l]>dep[r]) swap(l,r);
    	modify(1,id[l],id[r],x);
    	return;
    }
    
    ll que_chain(int l,int r)
    {
        ll ans=0;
        while(top[l]!=top[r])
        {
            if(dep[top[l]]<dep[top[r]]) swap(l,r);
            ans=(ans+(ll)query(1,id[top[l]],id[l]))%p;
            l=f[top[l]];
        }
        if(dep[l]>dep[r]) swap(l,r);
        ans=(ans+(ll)query(1,id[l],id[r]))%p;
        return ans%p;
    }
    
    void mod_son(int l,int x){modify(1,id[l],id[l]+tot[l]-1,x);}
    
    ll que_son(int l){return query(1,id[l],id[l]+tot[l]-1)%p;}
        
    int main()
    {
        read(n),read(m),read(root),read(p);
        for(int i=1;i<=n;i++) read(b[i]); 
        for(int i=1;i<n;i++) read(u),read(v),add(u,v),add(v,u);
    	dfs1(root,0),dfs2(root,root);
        for(int i=1;i<=n;i++) t[id[i]]=b[i];
        build(1,1,n);
        for(int i=1;i<=m;i++)
        {
            read(ty);
            if(ty==1) read(x),read(y),read(z),mod_chain(x,y,(ll)z%p);
            else if(ty==2) read(x),read(y),printf("%lld
    ",que_chain(x,y));
            else if(ty==3) read(x),read(y),mod_son(x,(ll)y%p);
            else read(x),printf("%lld
    ",que_son(x));
        }
        return 0;
    }
    

    - 树上差分(借用树剖实现)((mathcal O(nlog n))

    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define MAXN 50005
    #define read(x) scanf("%d",&x)
    
    int n,m;
    int u,v;
    int rt[MAXN];
    struct node
    {
        int to,nxt;   
    }e[MAXN<<1];
    int head[MAXN],cnt=0;
    int dep[MAXN],fa[MAXN],son[MAXN],top[MAXN],tot[MAXN];
    int id[MAXN],c=0;
    
    void add(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}
    
    int dfs1(int cur,int fat)
    {
        int maxn=0;
        tot[cur]=1,fa[cur]=fat,dep[cur]=dep[fa[cur]]+1;
        for(int i=head[cur];i;i=e[i].nxt)
        {
            int j=e[i].to;
            if(j==fat) continue;
            int op=dfs1(j,cur);
            if(op>maxn) maxn=op,son[cur]=j;
            tot[cur]+=op;
        }
        return tot[cur];
    }
    
    void dfs2(int cur,int topf)
    {
        top[cur]=topf,id[cur]=++c;
        if(son[cur]) dfs2(son[cur],topf);
        for(int i=head[cur];i;i=e[i].nxt)
        {
            int j=e[i].to;
            if(j==fa[cur]||j==son[cur]) continue;
            dfs2(j,j);
        }
    }
    
    void work(int l,int r)
    {
        while(top[l]!=top[r])
        {
            if(dep[top[l]]<dep[top[r]]) swap(l,r);
            rt[id[top[l]]]++,rt[id[l]+1]--;
            l=fa[top[l]];
        }
        if(dep[l]>dep[r]) swap(l,r);
        rt[id[l]]++,rt[id[r]+1]--;
        return;
    }
    
    int main()
    {
        read(n),read(m);
        for(int i=1;i<n;i++) read(u),read(v),add(u,v),add(v,u);
        dfs1(1,0),dfs2(1,1);
        for(int i=1;i<=m;i++) read(u),read(v),work(u,v);
        int now=0;
        for(int i=1;i<=n;i++) rt[i]=rt[i-1]+rt[i],rt[0]=max(rt[0],rt[i]);
        printf("%d
    ",rt[0]);
        return 0;
    }
    

    边化点,直接搞,边的编号可以由它连接的靠近根的节点决定。

    代码不给了。

    当然这题正解是 LCA。

    - 线性基(mathcal O(nlog S))

    这个东西过于谔谔,不属于考查内容?

    所以博主又鸽了。

    - 可持久化线段树(主席树)1

    - 可持久化线段树(主席树)2

    复杂度是 (mathcal O((n+m)log n))

    注意空间!

    //这是第二个题
    #include"iostream"
    #include"cstdio"
    #include"cmath"
    #include"algorithm"
    using namespace std;
    
    #define MAXN 200005
    #define read(x) scanf("%d",&x)
    
    struct Tree
    {
    	int ls,rs,sum;
    	Tree(){ls=rs=sum=0;}
    }a[MAXN*22];
    struct node
    {
    	int val,id;
    }b[MAXN];
    int c=0,t[MAXN];
    int n,m;
    int l,r,root[MAXN],opt=1,k;
    int cnt[MAXN];
    int re[MAXN];
    
    bool cmp(node n,node m){return n.val<m.val;}
    
    void hsh()
    {
    	int lst=0x7fffffff;
    	sort(b+1,b+n+1,cmp);
    	for(int i=1;i<=n;i++)
    	{
    		if(b[i].val!=lst) re[++c]=b[i].val,lst=b[i].val;
    		t[b[i].id]=c;
    	}
    	return;
    }
    
    void update(int k){a[k].sum=a[a[k].ls].sum+a[a[k].rs].sum;}
    
    int build_bone(int l,int r)
    {
    	opt++;
    	int rt=opt;
    	if(l==r) return rt;
    	int mid=(l+r)>>1;
    	a[k].ls=build_bone(l,mid),a[k].rs=build_bone(mid+1,r);
    	return rt;
    }
    
    int build_chain(int k,int x,int y,int l,int r)
    {
    	opt++;
    	int rt=opt;
    	if(l==r){a[rt].sum=a[k].sum+y;return rt;}
    	int mid=(l+r)>>1;
    	if(x<=mid) 
    	{
    		a[rt].rs=a[k].rs;
    		a[rt].ls=build_chain(a[k].ls,x,y,l,mid);
    	}
    	else 
    	{
    		a[rt].ls=a[k].ls;
    		a[rt].rs=build_chain(a[k].rs,x,y,mid+1,r);
    	}
    	update(rt);
    	return rt;
    }
    
    int find(int k,int w,int x,int l,int r)
    {
    	if(l==r) return l;
    	int mid=a[a[k].ls].sum-a[a[w].ls].sum;
    	int midd=(l+r)>>1;
    	if(x<=mid) return find(a[k].ls,a[w].ls,x,l,midd);
    	else return find(a[k].rs,a[w].rs,x-mid,midd+1,r);
    }
    
    int main()
    {
    	read(n),read(m);
    	for(int i=1;i<=n;i++) read(b[i].val),b[i].id=i;
    	hsh(),root[0]=build_bone(1,c);
    	for(int i=1;i<=n;i++) root[i]=build_chain(root[i-1],t[i],1,1,c);
    	while(m--)
    	{
    		read(l),read(r),read(k);
    		int now=find(root[r],root[l-1],k,1,c);
    		printf("%d
    ",re[now]);
    	}
    	return 0;
    }
    

    - 普通平衡树

    - 普通平衡树(数据加强版)

    都可以做到 (mathcal O(mlog n)) 实现。

    不会 splay,不会 treap,不会红黑树,也是够菜的的了。

    我只会很暴力的替罪羊树(ScapeGoat Tree),做就要做有挑战性的,所以下面是加强版的代码:

    //注意有三个函数是需要动态改点的,记得加上取地址符 
    #include"iostream"
    #include"cstdio"
    #include"cmath"
    using namespace std;
    
    #define alpha 0.75
    #define MAXN 1100005
    
    int n,m,root;
    int rt[MAXN],cnt=0;
    int tot=0;
    struct node
    {
    	int val,wn,sh,ls,rs,si;
    	node(){val=wn=sh=ls=rs=si=0;}
    }a[MAXN];
    int opt,x,y;
    int lst=0;
    int ans=0;
    
    inline int read()
    {
    	int x=0;
    	char c=getchar();
    	while(c<'0'||c>'9')c=getchar();
    	while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
    	return x;
    }
    
    bool judge(int k)
    {
    	return a[k].wn&&(a[k].sh<=alpha*a[k].si||max(a[a[k].ls].si,a[a[k].rs].si)>=alpha*a[k].si);
    }
    
    void update(int k)
    {
    	a[k].si=a[a[k].ls].si+a[a[k].rs].si+a[k].wn;
    	a[k].sh=a[a[k].ls].sh+a[a[k].rs].sh+a[k].wn;
    	return;
    }
    
    void unfold(int k)
    {
        if(!k) return;
        unfold(a[k].ls);
        if(a[k].wn) rt[++cnt]=k;
        unfold(a[k].rs);
        return;
    }
    
    int rebuild(int l,int r)
    {
    	if(l>=r) return 0;
    	int mid=(l+r)>>1;
    	a[rt[mid]].ls=rebuild(l,mid);
    	a[rt[mid]].rs=rebuild(mid+1,r); 
    	update(rt[mid]);
    	return rt[mid];
    }
    
    void bal(int &k){cnt=0,unfold(k);k=rebuild(1,cnt+1);}
    //上面内个+1可不要忘了呦
    
    void insert(int &k,int v)
    {
    	if(!k)
    	{
    		k=++tot;
    		if(!root) root=1;
    		a[k].val=v,a[k].sh=a[k].si=a[k].wn=1;
    		a[k].ls=a[k].rs=0;
    		return;
    	}
    	else if(a[k].val==v){a[k].wn++,a[k].sh++,a[k].si++;return;}
    	else
    	{
    		if(v<a[k].val) insert(a[k].ls,v);
    		else insert(a[k].rs,v);
    		update(k);
    		if(judge(k)) bal(k);
    	}
    }
    
    void del(int &k,int v)
    {
    	if(!k) return;
    	if(a[k].val==v){a[k].sh--,a[k].wn--;return;}
    	if(a[k].val>v) del(a[k].ls,v);
    	else del(a[k].rs,v);
    	update(k);
    	if(judge(k)) bal(k);
    }
    
    int rk(int k,int v)
    {
    	if(!k) return 0;
    	if(a[k].val==v) return a[a[k].ls].sh;
    	else if(a[k].val>v) return rk(a[k].ls,v);
    	else return a[a[k].ls].sh+a[k].wn+rk(a[k].rs,v);
    }
     
    int rkk(int k,int v)
    {
    	if(!k) return 1;
    	if(a[k].val==v) return 1+a[a[k].ls].sh+a[k].wn;//这里也要+1 
    	else if(a[k].val>v) return rkk(a[k].ls,v);
    	else return rkk(a[k].rs,v)+a[a[k].ls].sh+a[k].wn;
    }
    
    int at(int k,int x)
    {
    	if(a[k].ls==a[k].rs) return a[k].val;
    	if(x<=a[a[k].ls].sh) return at(a[k].ls,x);
    	else if(x<=a[a[k].ls].sh+a[k].wn) return a[k].val;
    	else return at(a[k].rs,x-a[a[k].ls].sh-a[k].wn);
    }
    
    int head(int v){return at(root,rk(root,v));}
    
    int back(int v){return at(root,rkk(root,v));}
    
    int main()
    {
    	n=read(),m=read();
    	for(int i=1;i<=n;i++) insert(root,read());
    	for(int i=1;i<=m;i++)
    	{
    		opt=read(),x=lst^read();
    		if(opt==1) insert(root,x);
    		else if(opt==2) del(root,x);
    		else if(opt==3) lst=(rk(root,x)+1);
    		else if(opt==4) lst=at(root,x);
    		else if(opt==5) lst=head(x);
    		else lst=back(x);
    		if(opt>=3) ans^=lst;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    顺便推广一下我的日报->替罪羊树学习笔记
    ,谢谢资瓷,点个赞啊!

    - 带修莫队(mathcal O(n^{frac{5}{3}}))

    这个就是一个三维莫队,一定考不到,而且还辣么卡常,所以就咕咕咕了。

  • 相关阅读:
    实验五 shell脚本编程
    实验四 Linux系统C语言开发环境学习
    实验三 Linux系统用户管理及VIM配置
    实验二 Linux系统简单文件操作命令
    实验一 Linux系统与应用准备
    实验八 进程间通信
    实验七 信号
    实验六 进程基础
    实验五 shell脚本编程
    实验四 Linux系统搭建C语言编程环境
  • 原文地址:https://www.cnblogs.com/tlx-blog/p/13899772.html
Copyright © 2011-2022 走看看