板子集合

放一块方便复习,以及一些懒得写博客的新学知识点

tarjan

点击查看代码
//缩点
void tarjan(int u){
    dfn[u]=low[u]=++t;
    s[++top]=u;vis[u]=1;
    for(int i=0;i<g[u].size();++i){
        int v=g[u][i];
        if(!dfn[v]){
            tarjan(v);low[u]=min(low[u],low[v]);
        }else  if(vis[v])  low[u]=min(low[u],low[v]);
    }if(dfn[u]==low[u]){
        vis[u]=0;
        c[++sum]++;fa[u]=sum;
        while(s[top]^u){
            vis[st[top]]=0;
            c[sum]++;fa[s[top--]]=sum;
        }--top;
    }
}
//割点
void tarjan(int u){
    dfn[u]=low[u]=++t;int f=0;
    for(int i=0;i<g[u].size();++i){
        int v=g[u][i];
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
            if(dfn[u]<=low[v])  ++f;
        }else  low[u]=min(low[u],dfn[v]);
    }if((u==rt&&f>1)||(u!=rt&&f))  q.push(u);
}
//割边
void tarjan(int u){
    dfn[u]=low[u]=++t;int f=0;
    for(int i=0;i<g[u].size();++i){
        int v=g[u][i];
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
            if(dfn[u]<low[v])  a[++cnt]={min(u,v),max(u,v)};
        }else  low[u]=min(low[u],dfn[v]);
    }
}
//点双
void tarjan(int u){
    dfn[u]=low[u]=++t;st[++top]=u;int f=0;
    for(int i=0;i<g[u].size();++i){
        int v=g[u][i];
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);++f;
            if(dfn[u]<=low[v]){
                a[++ans].push_back(u);
                while(st[top+1]!=v)  a[ans].push_back(st[top--]);
            }  
        }else  low[u]=min(low[u],dfn[v]);
    }if(u==rt&&!f)  a[++ans].push_back(u);
}
//边双
void tarjan(int u,int fa){
    dfn[u]=low[u]=++t;s[++top]=u;
    for(in ti=0;i<g[u].size();++i){
        int v=g[u][i];
        if(!dfn[v]){
            tarjan(v,u);
            if(low[v]>=dfn[u])  continue;
            low[u]=min(low[u],low[v]);
        }else  if(v^fa)  low[u]=min(low[u],low[v]);
    }if(dfn[u]==low[u]){
        vis[u]=0;fa[u]=++sum;
        while(s[top]^u){
            vis[s[top]]=0;f[s[top--]]=sum;
        }--top;
    }
}

2-SAT

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MAX=2e6+10;
int n,m,x,y,z1,z2,id[MAX][2],dfn[MAX],fa[MAX],low[MAX],s[MAX],t,top,vis[MAX],sum;
int tot,head[MAX];
struct edge{
    int t,nxt;
} e[MAX];
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c<='9'&&c>='0'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
inline void add(int a,int b){
	e[++tot].t=b;e[tot].nxt=head[a];head[a]=tot;
}void tarjan(int u){
	dfn[u]=low[u]=++t;s[++top]=u;vis[u]=1;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].t;
		if(!dfn[v]){
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}else  if(vis[v])  low[u]=min(low[u],dfn[v]);	
	}if(dfn[u]==low[u]){
		vis[u]=0;
		fa[u]=++sum;
		while(s[top]!=u){
			vis[s[top]]=0;
			fa[s[top--]]=sum;
		}top--;
	}
}
int main(){
    n=read();m=read();
    for(int i=1,p=0;i<=n;++i)
        for(int j=0;j<=1;++j)  id[i][j]=++p;
    for(int i=1;i<=m;++i){
        x=read();z1=read();y=read();z2=read();
        add(id[x][!z1],id[y][z2]);add(id[y][!z2],id[x][z1]);
    }for(int i=1;i<=n<<1;++i)
        if(!dfn[i])  tarjan(i);
    for(int i=1;i<=n;++i)
        if(fa[id[i][0]]==fa[id[i][1]]){
            cout<<"IMPOSSIBLE";return 0;
        }  
    printf("POSSIBLE\n");
    for(int i=1;i<=n;++i)
        cout<<(fa[id[i][0]]>fa[id[i][1]])<<" ";
    
}

二次剩余

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int T,n,mod,a,x0,x1,ii;
struct com{
    int r,i;
};
inline com operator*(com x,com y){
    return {(x.r*y.r+ii*x.i%mod*x.i%mod)%mod,(x.i*y.r+x.r*y.i)%mod};
}
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^'0');c=getchar();}
	return x;
}
inline int power(int a,int b){
    int res=1;
    while(b){
        if(b&1)  res=res*a%mod;
        a=a*a%mod;b>>=1;
    }return res;
}inline com power(com a,int b){
    com res={1,0};
    while(b){
        if(b&1)  res=res*a;
        a=a*a;b>>=1;
    }return res;
}inline void solve(){
    do{
        a=rand()%mod;
    }while(!a||power((a*a%mod-n+mod)%mod,mod-1>>1)==1);
    ii=(a*a%mod-n+mod)%mod;
    x0=power({a,1},mod+1>>1).r;x1=mod-x0;
}
signed main(){ 
    T=read();srand(time(0));
    while(T--){
        n=read();mod=read();
        if(n==0){printf("0\n");continue;}
        if(power(n,mod-1>>1)^1){printf("Hola!\n");continue;}
        solve();printf("%lld %lld\n",x0,x1);
    }
}
                                        

数据结构

带权并查集

int father(int x) { 
    if(x == fa[x])  return x;
    int t=fa[x];
    fa[x]=father(fa[x]);
    val[x]=max(val[x],val[t]);
    return fa[x];
}

std::priority_queue<TypeName, Container, Compare> q;
// 使用 Container 作为底层容器,使用 Compare 作为比较类型
auto cmp = [](const std::pair<int, int> &l, const std::pair<int, int> &r) {
  return l.second < r.second;
};
std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int> >,decltype(cmp)>pq(cmp);

std::priority_queue<int> q1;//大根堆
std::priority_queue<int, std::vector<int> > q2;
std::priority_queue<int, std::deque<int>, std::greater<int> > q3;//小根堆

分块

inline int query(){//区间求和
	ans=0;l=read();r=read();
	for(int i=l;i<=min(r,block[l]*(int)sqrt(n));++i)
		ans+=a[i];
	if(block[l]==block[r])  return ans;
	int b=(block[r]-1)*sqrt(n)+1;
	for(int i=b;i<=r;++i)  ans+=a[i];
	for(int i=block[l]+1;i<=block[r]-1;++i)  ans+=sum[i];
	return ans;
}
inline void add(int y){//单点加
	x=read();v=read()*y;
	a[x]+=v;sum[block[x]]+=v;
}

单调队列

int q[N],head,tail;
void Calc() //滑动窗口,求连续k个的最小值
{
	head=1,tail=0; //清空队列
	for(int i=1;i<=n;i++) //枚举窗口右端
	{
	  while(head<=tail && i-q[head]+1>k) q[head++]=0; //弹出已经离开窗口的元素
	  while(head<=tail && a[q[tail]]>a[i]) q[tail--]=0; //从队尾踢掉之前所有比当前元素大的数
	  q[++tail]=i; //当前元素自己加入队尾
	  if(i>=k) res[i-k+1]=q[head]; //完成以上操作后,队头即为最小值
	}
	return;
}

单调栈

数列中第 \(i\) 个元素之后第一个大于 \(a_i\) 的元素的下标

	for(long long i=1;i<=n+1;++i)
	{
		if(S.empty() || a[S.top()]>=a[i])	S.push(i);
		else
		{
			while(S.size() && a[S.top()]<a[i])
			{
				long long Top=S.top();
				ans+=(i-Top-1);
				S.pop();
			}
			S.push(i);
		}
	}

ST表

void init() {
	for (int i = 1; i <= n; i++) st[i][0] = a[i];
	for (int j = 1; (1<<j) <= n; j++) 
		for (int i = 1; i <= n - (1 << j) + 1; i++) 
			st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]); 
}
int ask(int l, int r) {
	int k = log2(r - l + 1);
	return max(st[l][k], st[r - (1 << k) + 1][k]);
}

树状数组

区间加,区间求和

int t1[MAXN], t2[MAXN], n;

int lowbit(int x) { return x & (-x); }

void add(int k, int v) {
  int v1 = k * v;
  while (k <= n) {
    t1[k] += v, t2[k] += v1;
    k += lowbit(k);
  }
}
int getsum(int *t, int k) {
  int ret = 0;
  while (k) {
    ret += t[k];
    k -= lowbit(k);
  }
  return ret;
}
void add1(int l, int r, int v) {
  add(l, v), add(r + 1, -v);  // 将区间加差分为两个前缀加
}
long long getsum1(int l, int r) {
  return (r + 1ll) * getsum(t1, r) - 1ll * l * getsum(t1, l - 1) -
         (getsum(t2, r) - getsum(t2, l - 1));
}

二维树状数组

单点加,查询子矩阵和

void add(int x, int y, int v) {
  for (int i = x; i <= n; i += lowbit(i)) {
    for (int j = y; j <= m; j += lowbit(j)) {
      c[i][j] += v;
    }
  }
}
int sum(int x, int y) {
  int res = 0;
  for (int i = x; i > 0; i -= lowbit(i)) {
    for (int j = y; j > 0; j -= lowbit(j)) {
      res += c[i][j];
    }
  }
  return res;
}

int ask(int x1, int y1, int x2, int y2) {
  // 查询子矩阵和
  return sum(x2, y2) - sum(x2, y1 - 1) - sum(x1 - 1, y2) + sum(x1 - 1, y1 - 1);
}

子矩阵加,求子矩阵和

using ll = long long;
ll t1[N][N], t2[N][N], t3[N][N], t4[N][N];

void add(ll x, ll y, ll z) {
  for (int X = x; X <= n; X += lowbit(X))
    for (int Y = y; Y <= m; Y += lowbit(Y)) {
      t1[X][Y] += z;
      t2[X][Y] += z * x;  // 注意是 z * x 而不是 z * X,后面同理
      t3[X][Y] += z * y;
      t4[X][Y] += z * x * y;
    }
}

void range_add(ll xa, ll ya, ll xb, ll yb,
               ll z) {  //(xa, ya) 到 (xb, yb) 子矩阵
  add(xa, ya, z);
  add(xa, yb + 1, -z);
  add(xb + 1, ya, -z);
  add(xb + 1, yb + 1, z);
}

ll ask(ll x, ll y) {
  ll res = 0;
  for (int i = x; i; i -= lowbit(i))
    for (int j = y; j; j -= lowbit(j))
      res += (x + 1) * (y + 1) * t1[i][j] - (y + 1) * t2[i][j] -
             (x + 1) * t3[i][j] + t4[i][j];
  return res;
}

ll range_ask(ll xa, ll ya, ll xb, ll yb) {
  return ask(xb, yb) - ask(xb, ya - 1) - ask(xa - 1, yb) + ask(xa - 1, ya - 1);
}

线段树

线段树基础

建树

void build(int pos,int l,int r)
{
    if(r<l)  return;
    if(l==r){scanf("%lf",&tree[pos].sum);return;}  
    int mid=l+r>>1;
    build(pos*2,l,mid);build(pos*2+1,mid+1,r);
    tree[pos].sum=tree[pos*2].sum+tree[pos*2+1].sum;
}

区间修改

void update(LL pos,LL l,LL r,LL ll,LL rr,LL num){
    if(ll<=l&&rr>=r){
        tree[pos].sum=tree[pos].sum*mod+(LL)x*(r-l+1);
        laz1[pos]*=mod;tree[pos].laz2=tree[pos].laz2*mod+num;
        return;
    }LL mid=(l+r)>>1;pushdown(pos,mid-l+1,r-mid);
    if(mid>=ll) update(pos<<1,l,mid,ll,rr,num);
    if(mid<rr) update(pos<<1|1,mid+1,r,ll,rr,num);
    tree[pos].sum=tree[pos<<1].sum+tree[pos<<1|1].sum;
}

懒标记(乘+加)

void pushdown(LL pos,LL len1,LL len2){
    if(tree[pos].laz2){
        laz1[pos<<1]*=laz1[pos];
        laz1[pos<<1|1]*=laz1[pos];
        tree[pos<<1].laz2=tree[pos<<1].laz2*laz1[pos]+tree[pos].laz2;
        tree[pos<<1|1].laz2=tree[pos<<1|1].laz2*laz1[pos]+tree[pos].laz2;
        tree[pos<<1].sum=tree[pos<<1].sum*laz1[pos]+tree[pos].laz2*len1;
        tree[pos<<1|1].sum=tree[pos<<1|1].sum*laz1[pos]+tree[pos].laz2*len2;
        laz1[pos]=1;tree[pos].laz2=0;
    }
}

懒标记(区间更改)

void pushdown(LL pos,LL l1,LL l2){
    if(tree[pos].laz){
        tree[pos<<1].laz=tree[pos].laz;
        tree[pos<<1|1].laz=tree[pos].laz;
        tree[pos<<1].sum=l1*tree[pos].laz;
        tree[pos<<1|1].sum=tree[pos].laz*l2;
        tree[pos].laz=0;
    }
}

区间查询

double query(long long pos,long long l,long long r,long long ll,long long rr)
{
    if(l>rr||r<ll)  return 0;
    if(ll<=l&&r<=rr)  return tree[pos].sum;
    long long mid=(l+r)>>1;
    pushdown(pos,mid-l+1,r-mid);
    return  query(pos*2,l,mid,ll,rr)+query(pos*2+1,mid+1,r,ll,rr);
}

单点查询

int query(int pos,int l,int r,int x){
    if(l==r)  return t[pos].sum;
    int mid=(l+r)>>1;
    if(mid>=x)  return query(t[pos].ls,l,mid,x);
    else  return query(t[pos].rs,mid+1,r,x);
}

线段树合并

int merge(int ra,int rb,int l,int r){
    if(!ra) return rb;
    if(!rb) return ra;
    if(l==r){
        tree[ra].sum1+=tree[rb].sum1;
        tree[ra].sum2+=tree[rb].sum2;
        tree[ra].sum3+=tree[rb].sum3;
        return ra;
    }
    int mid=(l+r)>>1;
    ls(ra)=merge(ls(ra),ls(rb),l,mid);
    rs(ra)=merge(rs(ra),rs(rb),mid+1,r);
    pushup(ra);return ra;
}

吉司机线段树

void update(int pos,int l,int r,int x,int v){
    if(x>=r){
        if(t[pos].mx<x)  return;
        if(t[pos].ma<x){
            t[pos].mx=t[pos].laz=v;return;
        }
    }int mid=l+r>>1;pushdown(pos);
    update(pos<<1,l,mid,x,v);
    if(x>mid)  update(pos<<1|1,mid+1,r,x,v);
    pushup(pos);
}int query(int pos,int l,int r,int x){
    if(l==r)  return t[pos].mx;
    int mid=l+r>>1;pushdown(pos);
    if(x<=mid)  return query(pos<<1,l,mid,x);
    else  return query(pos<<1|1,mid+1,r,x);
}void pushup(int pos){
    if(t[pos<<1].mx==t[pos<<1|1].mx){
        t[pos].mx=t[pos<<1].mx;t[pos].siz=t[pos<<1].siz+t[pos<<1|1].siz;
        t[pos].ma=max(t[pos<<1].ma,t[pos<<1|1].ma);return;
    }if(t[pos<<1].mx>t[pos<<1|1].mx){
        t[pos].mx=t[pos<<1].mx;t[pos].siz=t[pos<<1].siz;
        t[pos].ma=max(t[pos<<1].ma,t[pos<<1|1].mx);return;
    }t[pos].mx=t[pos<<1|1].mx;t[pos].siz=t[pos<<1|1].siz;
    t[pos].ma=max(t[pos<<1|1].ma,t[pos<<1].mx);
}void pushdown(int pos){
    if(t[pos].laz){
        int l1=t[pos<<1].mx;
        if(t[pos<<1].mx>=t[pos<<1|1].mx){
            t[pos<<1].mx=t[pos<<1].laz=t[pos].laz;
        }if(t[pos<<1|1].mx>=l1){
            t[pos<<1|1].mx=t[pos<<1|1].laz=t[pos].laz;
        }t[pos].laz=0;    
    }
}

李超线段树

要求在平面直角坐标系下维护两个操作:

  1. 在平面上加入一条线段。记第 \(i\) 条被插入的线段的标号为 \(i\)
  2. 给定一个数 \(k\),询问与直线 \(x = k\) 相交的线段中,交点纵坐标最大的线段的编号。
inline bool work(int q,int w,int e){
	if(!q)  return 0;
	double r=a[q].k*e+a[q].b,r1=a[w].k*e+a[w].b;
	return r>r1||(r1-r<eps&&q<w);
}

void update(int pos,int l,int r,int ll,int rr,int u){
	if(!u)  return;
	int mid=l+r>>1;
	if(ll<=l&&rr>=r){
		if(work(u,t[pos],mid))  swap(t[pos],u); 
		if(work(u,t[pos],l))  update(pos<<1,l,mid,ll,rr,u);
		if(work(u,t[pos],r))  update(pos<<1|1,mid+1,r,ll,rr,u);
		return;
	}
		if(ll<=mid)  update(pos<<1,l,mid,ll,rr,u);
		if(rr>mid)  update(pos<<1|1,mid+1,r,ll,rr,u);
}void query(int pos,int l,int r,int k){
	if(work(t[pos],ans,k))  ans=t[pos];
	if(l==r)  return;
	int mid=l+r>>1;
	if(k<=mid)  query(pos<<1,l,mid,k);
	else  query(pos<<1|1,mid+1,r,k);
	

平衡树

splay

  1. \(M\) 中插入一个数 \(x\)
  2. \(M\) 中删除一个数 \(x\)。(若有多个相同的数,应只删除一个)
  3. 查询 \(M\) 中有多少个数比 \(x\) 小,并且将得到的答案加 \(1\)
  4. 查询如果将 \(M\) 从小到大排列后,排名位于第 \(x\) 位的数。
  5. 查询 \(M\)\(x\) 的前驱(定义为 \(M\) 中小于 \(x\),且最大的数)。
  6. 查询 \(M\)\(x\) 的后继(定义为 \(M\) 中大于 \(x\),且最小的数)。
#include<bits/stdc++.h> 
using namespace std;
const int MAX=100010;
int son[MAX][2],val[MAX],fa[MAX],siz[MAX],cnt[MAX],tot,root,opt,x,n;
inline bool Dir(int x){return val[x]>val[fa[x]];}
void pushup(int x){ siz[x]=siz[son[x][0]]+siz[son[x][1]]+cnt[x]; }
inline void rotate(int x){
    int y=fa[x],z=fa[y],k=Dir(x),w=son[x][k^1];
    son[z][Dir(y)]=x; fa[x]=z;
    son[x][k^1]=y; fa[y]=x;
    son[y][k]=w; fa[w]=y;
    pushup(x); pushup(y);
}
inline void splay(int x,int goal=0){
    while(fa[x]!=goal){
        int y=fa[x],z=fa[y];
        if(z!=goal){
            if(Dir(x)==Dir(y))  rotate(y);
            else rotate(x);
        }rotate(x);
    }if(!goal)  root=x;
} 
inline void find(int x){
    if(!root)  return;
    int cur=root;
    while(son[cur][x>val[cur]]&&val[cur]!=x)
        cur=son[cur][x>val[cur]];
    splay(cur);
}
inline void ins(int x){
    int cur=root,p=0;
    while(cur&&val[cur]!=x)
        p=cur,cur=son[cur][x>val[cur]];
    if(cur) cnt[cur]++;
    else{
        cur=++tot;
        if(p) son[p][x>val[p]]=cur;
        son[cur][0]=son[cur][1]=0;
        val[cur]=x;fa[cur]=p;
        cnt[cur]=siz[cur]=1;
    }splay(cur);
} 
inline int las(int x){
    find(x);
    if(val[root]<x)  return root;
    int cur=son[root][0];
    while(son[cur][1])  cur=son[cur][1];
    splay(cur);return cur;
}
inline int nex(int x){
    find(x);
    if(val[root]>x)  return root;
    int cur=son[root][1];
    while(son[cur][0])  cur=son[cur][0];
    splay(cur);return cur;
}
inline void del(int x){
    int l=las(x),r=nex(x);//
    splay(l);splay(r,l);
    int d=son[r][0];
    if(cnt[d]>1){cnt[d]--;splay(d);}
    else son[r][0]=0;
    pushup(r);pushup(root);
} 
inline int getrk(int x){
    find(x);
    return siz[son[root][0]]+(val[root]<x?cnt[root]:0); 
} 
inline int getth(int k){
    int cur=root;
    while(1){
        if(k<=siz[son[cur][0]]&&son[cur][0])
            cur=son[cur][0];
        else
            if(k>siz[son[cur][0]]+cnt[cur]){
                k-=siz[son[cur][0]]+cnt[cur];
                cur=son[cur][1];
            }
            else{
                splay(cur);return val[cur];
            }
    } 
}
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*f;
}
int main(){
	//freopen("1.txt","r",stdin); 
	n=read();//cout<<n<<endl; 
	ins(-INT_MAX);ins(INT_MAX);
	for(int i=1;i<=n;++i){
		opt=read();x=read();
		if(opt==1)  ins(x);
		if(opt==2)  del(x);
		if(opt==3)  printf("%d\n",getrk(x));
		if(opt==4)  printf("%d\n",getth(x+1));
		if(opt==5)  printf("%d\n",val[las(x)]);
		if(opt==6)  printf("%d\n",val[nex(x)]);
		//cout<<i<<" "; 
	}
}

treap

旋转treap
#include <iostream>

constexpr int MAXN = 100005;
constexpr int INF = 1 << 30;

int n;

struct treap {  // 直接维护成数据结构,可以直接用
  int l[MAXN], r[MAXN], val[MAXN], rnd[MAXN], size_[MAXN], w[MAXN];
  int sz, ans, rt;

  void pushup(int x) { size_[x] = size_[l[x]] + size_[r[x]] + w[x]; }

  void lrotate(int &k) {
    int t = r[k];
    r[k] = l[t];
    l[t] = k;
    size_[t] = size_[k];
    pushup(k);
    k = t;
  }

  void rrotate(int &k) {
    int t = l[k];
    l[k] = r[t];
    r[t] = k;
    size_[t] = size_[k];
    pushup(k);
    k = t;
  }

  void insert(int &k, int x) {  // 插入
    if (!k) {
      sz++;
      k = sz;
      size_[k] = 1;
      w[k] = 1;
      val[k] = x;
      rnd[k] = rand();
      return;
    }
    size_[k]++;
    if (val[k] == x) {
      w[k]++;
    } else if (val[k] < x) {
      insert(r[k], x);
      if (rnd[r[k]] < rnd[k]) lrotate(k);
    } else {
      insert(l[k], x);
      if (rnd[l[k]] < rnd[k]) rrotate(k);
    }
  }

  bool del(int &k, int x) {  // 删除节点
    if (!k) return false;
    if (val[k] == x) {
      if (w[k] > 1) {
        w[k]--;
        size_[k]--;
        return true;
      }
      if (l[k] == 0 || r[k] == 0) {
        k = l[k] + r[k];
        return true;
      } else if (rnd[l[k]] < rnd[r[k]]) {
        rrotate(k);
        return del(k, x);
      } else {
        lrotate(k);
        return del(k, x);
      }
    } else if (val[k] < x) {
      bool succ = del(r[k], x);
      if (succ) size_[k]--;
      return succ;
    } else {
      bool succ = del(l[k], x);
      if (succ) size_[k]--;
      return succ;
    }
  }

  int queryrank(int k, int x) {
    if (!k) return 0;
    if (val[k] == x)
      return size_[l[k]] + 1;
    else if (x > val[k]) {
      return size_[l[k]] + w[k] + queryrank(r[k], x);
    } else
      return queryrank(l[k], x);
  }

  int querynum(int k, int x) {
    if (!k) return 0;
    if (x <= size_[l[k]])
      return querynum(l[k], x);
    else if (x > size_[l[k]] + w[k])
      return querynum(r[k], x - size_[l[k]] - w[k]);
    else
      return val[k];
  }

  void querypre(int k, int x) {
    if (!k) return;
    if (val[k] < x)
      ans = k, querypre(r[k], x);
    else
      querypre(l[k], x);
  }

  void querysub(int k, int x) {
    if (!k) return;
    if (val[k] > x)
      ans = k, querysub(l[k], x);
    else
      querysub(r[k], x);
  }
} T;

using std::cin;
using std::cout;

int main() {
  cin.tie(nullptr)->sync_with_stdio(false);
  srand(123);
  cin >> n;
  int opt, x;
  for (int i = 1; i <= n; i++) {
    cin >> opt >> x;
    if (opt == 1)
      T.insert(T.rt, x);
    else if (opt == 2)
      T.del(T.rt, x);
    else if (opt == 3) {
      cout << T.queryrank(T.rt, x) << '\n';
    } else if (opt == 4) {
      cout << T.querynum(T.rt, x) << '\n';
    } else if (opt == 5) {
      T.ans = 0;
      T.querypre(T.rt, x);
      cout << T.val[T.ans] << '\n';
    } else if (opt == 6) {
      T.ans = 0;
      T.querysub(T.rt, x);
      cout << T.val[T.ans] << '\n';
    }
  }
  return 0;
}
无旋treap

int n,tot,dl,dr,tmp,rt,opt,x;
struct node{
	int l,r,key,val,siz;
} t[MAX<<1];

inline int get(int x){
	t[++tot].key=x;t[tot].val=rand();
	t[tot].siz=1;return tot;
}inline void pushup(int pos){
	t[pos].siz=t[t[pos].l].siz+t[t[pos].r].siz+1;
}void split(int pos,int x,int &l,int &r){
	if(!pos){
		l=r=0;return;
	}if(t[pos].key<=x){
		l=pos;split(t[l].r,x,t[l].r,r);
	}else{
		r=pos;split(t[r].l,x,l,t[r].l);
	}pushup(pos);
}int merge(int l,int r){
	if(!l||!r)  return l|r;
	if(t[l].val<=t[r].val){
		t[l].r=merge(t[l].r,r);
		pushup(l);return l;
	}t[r].l=merge(l,t[r].l);
	pushup(r);return r;
}inline void insert(int x){
	split(rt,x-1,dl,dr);
	rt=merge(merge(dl,get(x)),dr);
}inline void del(int x){
	split(rt,x-1,dl,dr);
	split(dr,x,tmp,dr);
	tmp=merge(t[tmp].l,t[tmp].r);
	rt=merge(merge(dl,tmp),dr);
}inline int getrk(int x){
	split(rt,x-1,dl,dr);
	int rk=t[dl].siz+1;
	rt=merge(dl,dr);return rk;
}int getnum(int pos,int x){
	int num=t[t[pos].l].siz+1;
	if(num==x)  return t[pos].key;
	if(num>x)  return getnum(t[pos].l,x);
	return getnum(t[pos].r,x-num);
}inline int pre(int x){
	split(rt,x-1,dl,dr);
	int num=getnum(dl,t[dl].siz);
	rt=merge(dl,dr);return num;
}inline int nxt(int x){
	split(rt,x,dl,dr);
	int num=getnum(dr,1);
	rt=merge(dl,dr);return num;
}

可持久化

主席树

给出一个长度为n的序列a1an,有m次询问(x,y,k),每次询问a[x]a[y]内的第k小数。

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e5+10;
int n,m,k,c[MAX],opt,root[MAX],ll,rr,tot;
int a[MAX<<1],b[MAX<<1];
struct node{
	int sum,ls,rs;
} t[MAX<<5];
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
void update(int,int&,int,int,int);
int query(int,int,int,int,int);
int main(){
	//freopen("未命名4.txt","r",stdin); 
	//freopen("huashen.txt","w",stdout);
	n=read();m=read();
	for(int i=1;i<=n;++i)  b[i]=a[i]=read();
	sort(b+1,b+n+1);
	int l=unique(b+1,b+n+1)-b-1;
	for(int i=1;i<=n+m;++i)  
		a[i]=lower_bound(b+1,b+l+1,a[i])-b;
	for(int i=1;i<=n;++i)
		update(root[i-1],root[i],1,l,a[i]);
	for(int i=1;i<=m;++i){
		ll=read();rr=read();k=read();
		printf("%d\n",b[query(root[ll-1],root[rr],1,l,k)]);
	}
}
void update(int pre,int &pos,int l,int r,int x){
	pos=++tot;t[pos]=t[pre];
	t[pos].sum++;
	if(l==r)  return;
	int mid=(l+r)>>1;
	if(mid>=x)  update(t[pre].ls,t[pos].ls,l,mid,x);
	else  update(t[pre].rs,t[pos].rs,mid+1,r,x); 
}int query(int pre,int pos,int l,int r,int x){
	if(l==r)  return l;
	int mid=(l+r)>>1;
	int num=t[t[pos].ls].sum-t[t[pre].ls].sum;
	if(num>=x)  return query(t[pre].ls,t[pos].ls,l,mid,x);
	else  return query(t[pre].rs,t[pos].rs,mid+1,r,x-num);
}

可持久化平衡树

#include<bits/stdc++.h>
using namespace std;
const int MAX=5e5+10,inf=(1ll<<31)-1;
int n,v,op,x,rt[MAX],tot,dl,dr,tmp;
struct node{
	int ls,rs,key,val,siz;
} t[MAX<<5];
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c<='9'&&c>='0'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
inline int get(int x){t[++tot]={0,0,x,rand(),1};return tot;}
inline void pushup(int pos){t[pos].siz=t[t[pos].ls].siz+t[t[pos].rs].siz+1;}
void split(int pos,int x,int &l,int &r){
	if(!pos){l=r=0;return;}
	int now=++tot;t[now]=t[pos];
	if(t[now].key<=x){
		l=now;split(t[l].rs,x,t[l].rs,r);
	}else{
		r=now;split(t[r].ls,x,l,t[r].ls);
	}pushup(now);
}int merge(int l,int r){
	if(!l||!r)  return l|r;
	if(t[l].val<=t[r].val){
		t[l].rs=merge(t[l].rs,r);
		pushup(l);return l;
	}t[r].ls=merge(l,t[r].ls);
	pushup(r);return r;
}
inline void ins(int &rt,int x){
	split(rt,x-1,dl,dr);
	rt=merge(merge(dl,get(x)),dr);
}inline void del(int &rt,int x){
	split(rt,x-1,dl,tmp);split(tmp,x,tmp,dr);
	tmp=merge(t[tmp].ls,t[tmp].rs);
	rt=merge(merge(dl,tmp),dr);
}inline void getrk(int &rt,int x){
	split(rt,x-1,dl,dr);printf("%d\n",t[dl].siz);
	rt=merge(dl,dr);
}int getnum(int pos,int x){
	int num=t[t[pos].ls].siz+1;
	if(num==x)  return t[pos].key;
	if(num>x)  return getnum(t[pos].ls,x);
	return getnum(t[pos].rs,x-num);
}inline void getpre(int &rt,int x){
	split(rt,x-1,dl,dr);printf("%d\n",getnum(dl,t[dl].siz));
	rt=merge(dl,dr);
}inline void getnxt(int &rt,int x){
	split(rt,x,dl,dr);printf("%d\n",getnum(dr,1));
	rt=merge(dl,dr);
}

int main(){
    n=read();ins(rt[0],inf);ins(rt[0],-inf);
	for(int i=1;i<=n;++i){
		v=read();op=read();x=read();
		rt[i]=rt[v];
		if(op==1)  ins(rt[i],x);
		if(op==2)  del(rt[i],x);
		if(op==3)  getrk(rt[i],x);
		if(op==4)  printf("%d\n",getnum(rt[i],x+1));
		if(op==5)  getpre(rt[i],x);
		if(op==6)  getnxt(rt[i],x);
	}
}

可持久化trie树

  1. A x:添加操作,表示在序列末尾添加一个数 \(x\),序列的长度 \(N\)\(1\)
  2. Q l r x:询问操作,你需要找到一个位置 \(p\),满足 \(l \le p \le r\),使得:\(a[p] \oplus a[p+1] \oplus ... \oplus a[N] \oplus x\) 最大,输出最大值。
#include<bits/stdc++.h>
using namespace std;
const int MAX=6e5+10;
const int inf=1<<28;
int n,m,a[MAX],l,r,x,tr[MAX*28][2],cnt[MAX*28],ind,rt[MAX];
char op;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
inline void add(int x){
    int p=rt[x]=++ind,q=rt[x-1];cnt[p]=cnt[q]+1;
    for(int i=28;i>=0;--i){
        int u=(a[x]>>i)&1;
        tr[p][0]=tr[q][0];tr[p][1]=tr[q][1];
        p=tr[p][u]=++ind;q=tr[q][u];
        cnt[p]=cnt[q]+1;
    }
}inline int ask(int p,int q,int x){
    int res=0;
    for(int i=28;i>=0;--i){
        int u=(x>>i)&1;
        if(cnt[tr[p][u^1]]>cnt[tr[q][u^1]]){
            res|=1<<i;p=tr[p][u^1];q=tr[q][u^1];
        }else{p=tr[p][u];q=tr[q][u];}
    }return res;
}
signed main(){
    n=read();m=read();int p=rt[0]=++ind;cnt[p]=1;
    for(int i=28;i>=0;--i)  p=tr[p][0]=++ind,cnt[p]++;
    for(int i=1;i<=n;++i)  a[i]=read()^a[i-1],add(i);
    while(m--){
        cin>>op;
        if(op=='A'){a[++n]=read()^a[n];add(n);}
        else{
            l=read();r=read();x=read();
            cout<<ask(rt[r-1],l>=2?rt[l-2]:0,x^a[n])<<endl;
        }
    }

}

树套树

void update(int &rt,int l,int r,int x,int num){
	if(!rt)  rt=++tot;
	t[rt].sum+=num;
	if(l==r)  return;
	int mid=(l+r)>>1;
	if(x<=mid)  update(t[rt].ls,l,mid,x,num);
	else  update(t[rt].rs,mid+1,r,x,num);
}
int query(int l,int r,int k){
	if(l==r)  return l;
	int num=0;
	for(int i=1;i<=l2;++i)  num+=t[t[pos[i]].ls].sum;
	for(int i=1;i<=l1;++i)  num-=t[t[pre[i]].ls].sum;
	int mid=(l+r)>>1;
	if(num>=k){
		for(int i=1;i<=l1;++i)  pre[i]=t[pre[i]].ls;
		for(int i=1;i<=l2;++i)  pos[i]=t[pos[i]].ls;
		return query(l,mid,k);
	}else{
		for(int i=1;i<=l1;++i)  pre[i]=t[pre[i]].rs;
		for(int i=1;i<=l2;++i)  pos[i]=t[pos[i]].rs;
		return query(mid+1,r,k-num);
	}
} 

K-D Tree

#include <algorithm>
#include <iostream>
using namespace std;
constexpr int N(2e5), LG{18};

struct pt {
  int x[2];
  int v, sum;
  int l, r;
  int L[2], R[2];
} t[N + 5], l, h;

int rt[LG];
int b[N + 5], cnt;

void upd(int p) {
  t[p].sum = t[t[p].l].sum + t[t[p].r].sum + t[p].v;
  for (int k : {0, 1}) {
    t[p].L[k] = t[p].R[k] = t[p].x[k];
    if (t[p].l) {
      t[p].L[k] = min(t[p].L[k], t[t[p].l].L[k]);
      t[p].R[k] = max(t[p].R[k], t[t[p].l].R[k]);
    }
    if (t[p].r) {
      t[p].L[k] = min(t[p].L[k], t[t[p].r].L[k]);
      t[p].R[k] = max(t[p].R[k], t[t[p].r].R[k]);
    }
  }
}

int build(int l, int r, int dep = 0) {
  int p{(l + r) >> 1};
  nth_element(b + l, b + p, b + r + 1,
              [dep](int x, int y) { return t[x].x[dep] < t[y].x[dep]; });
  int x{b[p]};
  if (l < p) t[x].l = build(l, p - 1, dep ^ 1);
  if (p < r) t[x].r = build(p + 1, r, dep ^ 1);
  upd(x);
  return x;
}

void append(int &p) {
  if (!p) return;
  b[++cnt] = p;
  append(t[p].l);
  append(t[p].r);
  p = 0;
}

int query(int p) {
  if (!p) return 0;
  bool flag{false};
  for (int k : {0, 1}) flag |= (!(l.x[k] <= t[p].L[k] && t[p].R[k] <= h.x[k]));
  if (!flag) return t[p].sum;
  for (int k : {0, 1})
    if (t[p].R[k] < l.x[k] || h.x[k] < t[p].L[k]) return 0;
  int ans{0};
  flag = false;
  for (int k : {0, 1}) flag |= (!(l.x[k] <= t[p].x[k] && t[p].x[k] <= h.x[k]));
  if (!flag) ans = t[p].v;
  return ans += query(t[p].l) + query(t[p].r);
}

int main() {
  int n;
  cin >> n;
  int lst{0};
  n = 0;
  while (true) {
    int op;
    cin >> op;
    if (op == 1) {
      int x, y, A;
      cin >> x >> y >> A;
      x ^= lst;
      y ^= lst;
      A ^= lst;
      t[++n] = {{x, y}, A};
      b[cnt = 1] = n;
      for (int sz{0};; ++sz)
        if (!rt[sz]) {
          rt[sz] = build(1, cnt);
          break;
        } else
          append(rt[sz]);
    } else if (op == 2) {
      cin >> l.x[0] >> l.x[1] >> h.x[0] >> h.x[1];
      l.x[0] ^= lst;
      l.x[1] ^= lst;
      h.x[0] ^= lst;
      h.x[1] ^= lst;
      lst = 0;
      for (int i{0}; i < LG; ++i) lst += query(rt[i]);
      cout << lst << "\n";
    } else
      break;
  }
  return 0;
}

LCT

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e5+10;
#define ls son[x][0]
#define rs son[x][1]
int f[MAX],son[MAX][2],a[MAX],val[MAX],st[MAX],laz[MAX],n,m,op,x,y;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^'0');c=getchar();}
	return x*f;
}
inline bool nrt(int x){return son[f[x]][0]==x||son[f[x]][1]==x;}
inline void pushup(int x){val[x]=val[ls]^val[rs]^a[x];}
inline void rev(int x){ls^=rs;rs^=ls;ls^=rs;laz[x]^=1;}
inline void pushdown(int x){
    if(laz[x]){
        rev(ls);rev(rs);laz[x]=0;
    }
}inline void rotate(int x){
    int y=f[x],z=f[y],k=son[y][1]==x,w=son[x][!k];
    if(nrt(y))  son[z][son[z][1]==y]=x;
    son[x][!k]=y;son[y][k]=w;
    if(w)  f[w]=y;f[y]=x;f[x]=z;
    pushup(y);pushup(x);
}inline void splay(int x){
    int y=x,z=0;
    st[++z]=y;
    while(nrt(st[z]))  st[++z]=f[st[z]];
    while(z)  pushdown(st[z--]);
    while(nrt(x)){
        y=f[x];z=f[y];
        if(nrt(y)){
            if((son[y][1]==x)^(son[z][1]==y))  rotate(x);
            else  rotate(y);
        }rotate(x);
    }
}inline void access(int x){
    for(int y=0;x;y=x,x=f[x]){
        splay(x);rs=y;pushup(x);
    }
}inline void markrt(int x){
    access(x);
    splay(x);
    rev(x);
}inline int findrt(int x){
    access(x);splay(x);
    while(ls)  pushdown(x),x=ls;
    splay(x);return x;
}inline void split(int x,int y){
    markrt(x);
    access(y);splay(y);
}inline void link(int x,int y){
    markrt(x);
    if(findrt(y)^x)  f[x]=y;
}inline void cut(int x,int y){
    markrt(x);
    if(findrt(y)==x&&f[y]==x&&!son[y][0]){
        f[y]=rs=0;pushup(x);
    }
}
signed main(){ 
	n=read();m=read();
    for(int i=1;i<=n;++i)  a[i]=read();
    while(m--){
        op=read();x=read();y=read();
        if(op==0){split(x,y);printf("%d\n",val[y]);}
        if(op==1)  link(x,y);
        if(op==2)  cut(x,y);
        if(op==3){splay(x);a[x]=y;pushup(x);}
    }
}



posted @ 2024-01-23 20:13  yisiwunian  阅读(62)  评论(0)    收藏  举报