模板整理——数据结构部分
- 数据结构
- 并查集
- 路径压缩实现(\(\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;
}
其他操作:
区间最大子段和,单点修改。
区间加等差数列,单点查询。
如果是区间求和就记录一下首项与公差,然后按等差数列性质向下更新。
权值线段树,为主席树开路。
线段树问题多合一。
线段树上二分。
区间开根。(这是一种思想,如区间除定值大数,区间取 \(\log\) 都可以用相似的实现方法)。
理论复杂度是 \(\mathcal O(\text{开根次数}\times n+n\log n)\)。
区间加斐波那契数列。
我们考虑到斐波那契数列递推式的可加性,两组合并为一组也是可以的。
所以考虑维护一下新的数列的前两项,可以反映整个数列。
- 莫队(\(\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;
}
- 重链剖分(\(\mathcal O(n\log^2n)\))
#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}})\))
这个就是一个三维莫队,一定考不到,而且还辣么卡常,所以就咕咕咕了。