……

模板整理——数据结构部分

- 数据结构

- 并查集

  • 路径压缩实现(\(\mathcal O(n\alpha (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(n\log 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(n\log 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\n",max(st[l][rt],st[r-(1<<rt)+1][rt]));
	}
	return 0;
}

- 树状数组1\(\mathcal O(m\log n)\)

- 树状数组2\(\mathcal O(m\log 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\n",query(y)-query(x-1));
	}
	return 0;
}
  • 利用树状数组实现区间加和区间求和

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

考虑做前缀和:

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

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

- 线段树1

- 线段树2

都是 \(\mathcal O(m\log 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\n",query(1,l,r,1,n));
    }
    return 0;
}

其他操作:

小白逛公园

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

无聊的数列

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

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

黑匣子

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

序列操作

线段树问题多合一。

冒泡排序

线段树上二分。

GSS4

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

理论复杂度是 \(\mathcal O(\text{开根次数}\times n+n\log n)\)

CF446C

区间加斐波那契数列。

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

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

  • 莫队\(\mathcal O(m\sqrt{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\n",ans[i]);
    return 0;
}

- 可并堆\(\mathcal O(n\log 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\n");
            else
            {
                printf("%d\n",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\n"); 
			else printf("%d\n",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\n",que_chain(x,y));
        else if(ty==3) read(x),read(y),mod_son(x,(ll)y%p);
        else read(x),printf("%lld\n",que_son(x));
    }
    return 0;
}

- 树上差分(借用树剖实现)(\(\mathcal O(n\log 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\n",rt[0]);
    return 0;
}

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

代码不给了。

当然这题正解是 LCA。

- 线性基\(\mathcal O(n\log 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\n",re[now]);
	}
	return 0;
}

- 普通平衡树

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

都可以做到 \(\mathcal O(m\log 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\n",ans);
	return 0;
}

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

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

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

posted @ 2020-10-29 23:00  童话镇里的星河  阅读(149)  评论(0编辑  收藏  举报