板子们~缓慢更新
1.树链剖分
#include<iostream> #include<cstdio> #define maxn 1000001 using namespace std; int num,head[maxn],dep[maxn],fa[maxn],siz[maxn]; int son[maxn],id[maxn],cnt,wt[maxn],w[maxn],top[maxn]; int sum[maxn],mo,laz[maxn],n,res,m,number; struct asd{ int next; int to; } a[maxn<<1]; inline void add(int x,int y) { a[++num].next=head[x]; a[num].to=y; head[x]=num; } inline void dfs1(int x,int father,int deep) { dep[x]=deep; fa[x]=father; siz[x]=1; int maxson=-1; for(int i=head[x];i;i=a[i].next) { int to=a[i].to; if(to==father) continue; dfs1(to,x,deep+1); siz[x]+=siz[to]; if(siz[to]>maxson) { son[x]=to; maxson=siz[to]; } } } inline void dfs2(int x,int topp) { id[x]=++cnt; wt[cnt]=w[x]; top[x]=topp; if(!son[x]) return; dfs2(son[x],topp); for(int i=head[x];i;i=a[i].next) { if(a[i].to==fa[x] || a[i].to==son[x]) continue; dfs2(a[i].to,a[i].to); } } inline void build(int rt,int l,int r) { if(l==r) { sum[rt]=wt[l]; sum[rt]%=mo; return; } int mid=l+r>>1; build(rt<<1,l,mid); build(rt<<1|1,mid+1,r); sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%mo; } inline void pushdown(int rt,int ln,int rn) { if(laz[rt]) { laz[rt<<1]+=laz[rt]; laz[rt<<1|1]+=laz[rt]; sum[rt<<1]+=laz[rt]*ln; sum[rt<<1|1]+=laz[rt]*rn; sum[rt<<1]%=mo; sum[rt<<1|1]%=mo; laz[rt]=0; } } inline void update(int rt,int l,int r,int L,int R,int k) { if(L<=l && r<=R) { laz[rt]+=k; sum[rt]+=k*(r-l+1); return; } int mid=l+r>>1; pushdown(rt,mid-l+1,r-mid); if(L<=mid) update(rt<<1,l,mid,L,R,k); if(R>mid) update(rt<<1|1,mid+1,r,L,R,k); sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%mo; } inline void change(int x,int y,int k) { k%=mo; while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]]) swap(x,y); update(1,1,n,id[top[x]],id[x],k); x=fa[top[x]]; } if(dep[x]>dep[y]) swap(x,y); update(1,1,n,id[x],id[y],k); } inline void query(int rt,int l,int r,int L,int R) { if(L<=l && r<=R) { res+=sum[rt]; res%=mo; return; } int mid=l+r>>1; pushdown(rt,mid-l+1,r-mid); if(L<=mid) query(rt<<1,l,mid,L,R); if(R>mid) query(rt<<1|1,mid+1,r,L,R); sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%mo; } inline int addup(int x,int y) { int ans=0; while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]]) swap(x,y); res=0; query(1,1,n,id[top[x]],id[x]); ans+=res; ans%=mo; x=fa[top[x]]; } if(dep[x]>dep[y]) swap(x,y); res=0; query(1,1,n,id[x],id[y]); ans+=res; return ans%=mo; } inline void changeS(int x,int k) { update(1,1,n,id[x],id[x]+siz[x]-1,k); } inline int addupS(int x) { res=0; query(1,1,n,id[x],id[x]+siz[x]-1); return res; } int main() { cin>>n>>m>>number>>mo; for(int i=1;i<=n;i++) scanf("%d",&w[i]); for(int i=1;i<=n-1;i++) { int aa,bb; scanf("%d%d",&aa,&bb); add(aa,bb); add(bb,aa); } dfs1(number,0,1); dfs2(number,number); build(1,1,n); while(m--) { int k,x,y,z; scanf("%d",&k); if(k==1) { scanf("%d%d%d",&x,&y,&z); change(x,y,z); } if(k==2) { scanf("%d%d",&x,&y); printf("%d\n",addup(x,y)); } if(k==3) { scanf("%d%d",&x,&y); changeS(x,y); } if(k==4) { scanf("%d",&x); printf("%d\n",addupS(x)); } } }
2.SA
#include<iostream> #include<cstdio> #include<cstring> #define maxn 1000010 using namespace std; int num[maxn],sa[maxn],se[maxn],rk[maxn],n,m,height[maxn]; char ch[maxn]; void debug() { printf("*****************\n"); printf("下标");for(int i=1;i<=n;i++) printf("%d ",i);printf("\n"); printf("sa ");for(int i=1;i<=n;i++) printf("%d ",sa[i]);printf("\n"); printf("rk ");for(int i=1;i<=n;i++) printf("%d ",rk[i]);printf("\n"); printf("se ");for(int i=1;i<=n;i++) printf("%d ",se[i]);printf("\n"); } inline void rsort() { for(int i=0;i<=m;i++) num[i]=0; for(int i=1;i<=n;i++) num[rk[i]]++; for(int i=1;i<=m;i++) num[i]+=num[i-1]; for(int i=n;i>=1;i--) sa[ num[ rk[ se[i]]]--]=se[i]; } inline void SA() { rsort(); for(int k=1,tot=1; tot<n; m=tot,k<<=1) { tot=0; for(int i=1;i<=k;i++) se[++tot]=n-k+i; for(int i=1;i<=n;i++) if(sa[i]>k) se[++tot]=sa[i]-k; rsort(); swap(rk,se); rk[sa[1]]=tot=1; for(int i=2;i<=n;i++) if(se[sa[i-1]]==se[sa[i]] && se[sa[i]+k]==se[sa[i-1]+k]) rk[sa[i]]=tot; else rk[sa[i]]=++tot; } for(int i=1;i<=n;i++) printf("%d ",sa[i]); } inline void HEIGHT() { int k=0; for(int i=1;i<=n;i++) rk[sa[i]]=i; for(int i=1,j;i<=n;i++) { if(rk[i]==1) continue; if(k) --k; j=sa[rk[i]-1]; while(j+k<=n && i+k<=n && ch[i+k]==ch[j+k]) k++; height[rk[i]]=k; } for(int i=1;i<=n;i++) printf("%d ",height[i]); } int main() { cin>>ch; n=strlen(ch); m=127; for(int i=0;i<=n-1;i++) { rk[i+1]=ch[i]-48; se[i+1]=i+1; } SA(); // HEIGHT(); return 0; }
3.KMP
#include<iostream> #include<cstdio> #include<cstring> using namespace std; char s[1000001],t[1000001]; int m,n,next[1000001]; int main() { scanf("%s",s); n=strlen(s); scanf("%s",t); m=strlen(t); next[0]=-1; int k=-1; for(int i=1;i<m;i++) { while(k>=0 && t[i]!=t[k+1]) k=next[k]; if(t[i]==t[k+1]) k++; next[i]=k; } int kk=-1; int ans=0; for(int i=0;i<n;i++) { while(kk>=0 && s[i]!=t[kk+1]) kk=next[kk]; if(s[i]==t[kk+1]) kk++; if(kk==m-1) { cout<<i+1-m+1<<endl;kk=next[kk]; } } for(int i=0;i<=m-1;i++) cout<<next[i]+1<<" "; return 0; }
4.treap
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; struct asd{ int ls,rs; //左右儿子 int size; // 统计包含自身的节点个数 int cnt; // 统计自己出现的次数 int dep; // 随机出来的优先级 int val; // 自身的值 } tree[10000001]; int root,size,n,ans; void update(int k) // 更新节点的个数 { tree[k].size=tree[tree[k].ls].size+tree[tree[k].rs].size+tree[k].cnt; return; // 记住上面的式子加上自身的重复 } void left(int &k) // 将其的右儿子左转 { int rson=tree[k].rs; tree[k].rs=tree[rson].ls; // 将自己的右儿子变为其右儿子的左儿子 tree[rson].ls=k; // 把自己变为其右儿子的左儿子 tree[rson].size=tree[k].size; // 然后在统计子树大小 update(k); // 并且更新其节点 k=rson; // 这个是将k的父亲把k这个儿子改成了rson } /* 解释一下这个k到底是什么 其实这个k相当于一个地址,它所储存了一堆信息,然后我们修改了这个指针所对应的值 但没有改变这个指针里面的值,只是修改了指针让它指向了另外一个点。指向了另个位置所对应的值 */ void right(int &k) { int lson=tree[k].ls; tree[k].ls=tree[lson].rs; tree[lson].rs=k; tree[lson].size=tree[k].size; update(k); k=lson; } // 与left同理 void insert(int &k,int x) { if(k==0) // 已经递归到了叶子节点 { size++; // 总的节点数+1 k=size; tree[k].size=1; tree[k].cnt=1; tree[k].val=x; tree[k].dep=rand(); // 随机一个堆:小根堆 return; } tree[k].size++; // 如果没到叶节点,说明此节点可包含所插入的数 if(tree[k].val==x) { tree[k].cnt++; return; } // 如果相等直接加上重复 if(tree[k].val<x) // 如果小于放到右子树上 { insert(tree[k].rs,x); // 插入右子树 if(tree[tree[k].rs].dep<tree[k].dep) // 违反了堆的性质 left(k); // 将k的rson转到k上去,把k转下来 } if(tree[k].val>x) // 放到左子树 { insert(tree[k].ls,x); if(tree[tree[k].ls].dep<tree[k].dep) right(k); } } void delet(int &k,int x) // 删除值等于x这个节点 { if(tree[k].val==x) // 找到了这个节点 { if(tree[k].cnt>1) // 如果有重复的话就直接操作 { tree[k].cnt--; tree[k].size--; return; } if(tree[k].ls*tree[k].rs==0) // 如果他没有重复且左右子树至少有一个0 k=tree[k].ls+tree[k].rs; else if(tree[tree[k].ls].dep<tree[tree[k].rs].dep) // 两个子树都有,找到优先级最大然后转上去 { right(k); delet(k,x); } // 此时的k已经变为叶子节点就可以直接删去 else { left(k); delet(k,x); } } else { if(tree[k].val>x) { tree[k].size--; delet(tree[k].ls,x); } else { tree[k].size--; delet(tree[k].rs,x); } // 一直找下去直到k为叶子节点然后删除 } } int check1(int &k,int x) { if(k==0) return 0; if(tree[k].val==x) return tree[tree[k].ls].size+1; // 如果找到了那么就是其左子树的个数+1 if(tree[k].val<x) return tree[tree[k].ls].size+tree[k].cnt+check1(tree[k].rs,x); if(tree[k].val>x) return check1(tree[k].ls,x); } int check2(int &k,int x) // 寻找排名为x的值 { if(x<=tree[tree[k].ls].size) return check2(tree[k].ls,x); // 如果排名小于当前节点左子树的大小,就在他的左子树中找 if(x>tree[tree[k].ls].size+tree[k].cnt) return check2(tree[k].rs,x-tree[tree[k].ls].size-tree[k].cnt); // 如果排名大于当前节点左子树+他的重复的大小,就在他的右子树中找 return tree[k].val; } void min_max(int k,int x) // 找比x小的最大数 { if(k==0) return; // 如果找到没有 if(tree[k].val<x) // 如果比此节点大 { ans=k; min_max(tree[k].rs,x); } // 先赋值,然后再去找更优解 else min_max(tree[k].ls,x); // 如果小了就只能在其左子树中找 } void max_min(int k,int x) // 找比x大的最小值 { if(k==0) return; if(tree[k].val>x) { ans=k; max_min(tree[k].ls,x); } else max_min(tree[k].rs,x); } int main() { cin>>n; int number,x; for(int i=1;i<=n;i++) { scanf("%d%d",&number,&x); if(number==1) insert(root,x); if(number==2) delet(root,x); if(number==3) printf("%d\n",check1(root,x)); if(number==4) printf("%d\n",check2(root,x)); if(number==5) { min_max(root,x); printf("%d\n",tree[ans].val); } if(number==6) { max_min(root,x); printf("%d\n",tree[ans].val); } } return 0; }
5.splay(大常数平衡树)
#include<iostream> #include<cstdio> using namespace std; int root,cnt,n,ans; struct asd{ int s[2]; int fa; int key; int cnt; int pos; int size; } tree[2000001]; inline void clear(int x) { tree[x].s[0]=tree[x].s[1]=tree[x].fa=tree[x].cnt=tree[x].key=tree[x].size=0; } // 将当前点的各项值都清0 (用于删除之后) inline int get(int x) { return tree[tree[x].fa].s[1]==x; } // 判断当前点是他父节点的左儿子还是右儿子 inline void pushup(int x) { if(x) { tree[x].size=tree[x].cnt; if(tree[x].s[0]) tree[x].size+=tree[tree[x].s[0]].size; if(tree[x].s[1]) tree[x].size+=tree[tree[x].s[1]].size; } } // 更新当前点的size值 (用于发生修改之后) inline void rotate(int x) { int old=tree[x].fa,oldf=tree[old].fa,which=get(x); tree[old].s[which]=tree[x].s[which^1]; if(tree[old].s[which]) tree[tree[old].s[which]].fa=old; tree[x].s[which^1]=old; tree[old].fa=x; tree[x].fa=oldf; if(oldf) tree[oldf].s[tree[oldf].s[1]==old]=x; pushup(old); pushup(x); } // 一共包含三个步骤。。。真●恶心 // step1:找出要变动节点(D)的父亲结点(B)以及父亲的父亲(A)并记录。。。判断D是B的左结点还是右结点 // step2:转换节点保证二叉树的大小关系不变。。。笼统概括(雾 ↓ /* 我们已经判断了D是B的左儿子还是右儿子,设这个关系为K; 将D与K关系相反的儿子的父亲记为B与K关系相同的儿子(这里即为D的右儿子的父亲记为B的左儿子) 将D与K关系相反的儿子的父亲即为B(这里即为把G的父亲记为B) 将B的父亲即为D 将D与K关系相反的儿子记为B(这里即为把D的右儿子记为B) 将D的父亲记为A。 最后要判断,如果A存在(即rotate到的位置不是根的话),要把A的儿子记为D。 显而易见,rotate之后所有牵涉到变化的父子关系都要改变。 以上的树需要改变四对父子关系,BG DG BD AB,需要三个操作(BG BD AB)。 */ // step3:pushup一下当前点和各个父结点的各个值 inline void splay(int x) { for(int fa;fa=tree[x].fa;rotate(x)) if(tree[fa].fa) rotate(get(x)==get(fa) ? fa:x); root=x; } // 实际上splay只是rotate的发展。。。就是不停的rotate。。。 // 因为没有确切的停止状态。。。此处代码直接到根 // 当然splay也会遇到一些特殊的情况。。。 // 如果是三点一条链的情况就需要先rotate x的父亲 // 不这样做会形成单旋使平衡树失衡。。。 /* inline void splay(int x,int tar) { for(int fa;(fa=tree[x].fa!=tar;rotate(x)) if(tree[fa]!=tar) rotate(get(x)==get(fa) ? fa:x); if(!tar) root=x; } */ inline void insert(int x) { if(root==0) { root=++cnt; tree[root].s[0]=0; tree[root].s[1]=0; tree[root].fa=0; tree[root].key=x; tree[root].cnt=1; tree[root].size=1; return; } int now=root,fa=0; while(1) { if(tree[now].key==x) { tree[now].cnt++; pushup(now); pushup(fa); splay(now); break; } fa=now; now=tree[now].s[tree[now].key<x]; if(now==0) { int k=++cnt; tree[k].s[0]=tree[k].s[1]=0; tree[k].key=x; tree[k].size=1; tree[k].cnt=1; tree[k].fa=fa; tree[fa].s[tree[fa].key<x]=k; pushup(fa); splay(k); break; } } } // 如果k==0 即树为空,做一些处理直接返回。。。 // 如果遇到一个结点的关键字等于当前要插入的点的话 // 我们就等于把这个结点加了一个权值 // 因为在二叉搜索树中是不可能出现两个相同的点 // 并且要将当前点和它父亲结点的各项值更新一下。做一下splay。 // 如果已经到了最底下了,那么就可以直接插入 // 整个树的大小要+1,新结点的左儿子右儿子(虽然是空)父亲还有各项值要一一对应 // 并且最后要做一下他父亲的update(做他自己的没有必要)。做一下splay inline int pre() { int x=tree[root].s[0]; while(tree[x].s[1]) x=tree[x].s[1]; return x; } inline int nxe() { int x=tree[root].s[1]; while(tree[x].s[0]) x=tree[x].s[0]; return x; } inline int find(int x) // 查询x的排名 { int k=root;ans=0; while(1) { if(x<tree[k].key) k=tree[k].s[0]; else { ans+=(tree[k].s[0] ? tree[tree[k].s[0]].size : 0); if(tree[k].key==x) { splay(k); return ans+1; } ans+=tree[k].cnt; k=tree[k].s[1]; } } } // inline int findx(int x) // 查询排名为x的点 { int k=root; while(1) { if(x<=tree[tree[k].s[0]].size && tree[k].s[0]) k=tree[k].s[0]; else { x-=tree[tree[k].s[0]].size+tree[k].cnt; x=max(x,0); if(!x) return tree[k].key; k=tree[k].s[1]; } } } // inline void delet(int x) { int hhh=find(x); if(tree[root].cnt>1) { tree[root].cnt--; return; } if(!tree[root].s[0] && !tree[root].s[1]) {clear(root); root=0; return; } if(!tree[root].s[0]) { int oldf=root; root=tree[root].s[1]; tree[root].fa=0; clear(oldf); return; } else if(!tree[root].s[1]) { int oldf=root; root=tree[root].s[0]; tree[root].fa=0; clear(oldf); return; } int leftbig=pre(),oldf=root; splay(leftbig); tree[tree[oldf].s[1]].fa=root; tree[root].s[1]=tree[oldf].s[1]; clear(oldf); pushup(root); return; } int main() { cin>>n; int opt,x; for(int i=1;i<=n;i++) { scanf("%d%d",&opt,&x); if(opt==1) insert(x); if(opt==2) delet(x); if(opt==3) printf("%d\n",find(x)); if(opt==4) printf("%d\n",findx(x)); if(opt==5) { insert(x); printf("%d\n",tree[pre()].key); delet(x); } if(opt==6) { insert(x); printf("%d\n",tree[nxe()].key); delet(x); } } }
6.splay(区间翻转)
#include<iostream> #include<cstdio> #define INF 99999999 #define maxn 1000001 using namespace std; int cnt,w[maxn],root,n,m,sz; bool mark[maxn]; struct asd{ int s[2]; int key; int fa; int size; } tree[maxn<<1]; inline void pushup(int x) { tree[x].size=tree[tree[x].s[0]].size+tree[tree[x].s[1]].size+1; } inline void swap(int &x,int &y) { int t=x; x=y; y=t; } inline bool get(int x) { return tree[tree[x].fa].s[1]==x; } inline void pushdown(int x) { if(x && mark[x]) { mark[tree[x].s[0]]^=1; mark[tree[x].s[1]]^=1; swap(tree[x].s[0],tree[x].s[1]); // ??? mark[x]=0; } } inline int build(int fa,int l,int r) { if(l>r) return 0; int mid=l+r>>1; int now=++sz; tree[now].key=w[mid]; tree[now].fa=fa; mark[now]=0; tree[now].s[0]=build(now,l,mid-1); tree[now].s[1]=build(now,mid+1,r); pushup(now); return now; } inline void rotate(int x) { int old=tree[x].fa , oldf=tree[old].fa , which=get(x); pushdown(old); pushdown(x); tree[old].s[which] = tree[x].s[which^1]; if (tree[old].s[which]) tree[tree[old].s[which]].fa = old; tree[old].fa=x; tree[x].fa=oldf; tree[x].s[which^1]=old; if(oldf) tree[oldf].s[tree[oldf].s[1]==old] = x; pushup(old); pushup(x); } inline void splay(int x,int tar) { for(int fa;(fa=tree[x].fa)!=tar;rotate(x)) if(tree[fa].fa!=tar) rotate(get(fa)==get(x) ? fa:x); if(!tar) root=x; } inline int findx(int k,int x) { while(1) { pushdown(k); if(x<=tree[tree[k].s[0]].size) k=tree[k].s[0]; else { x-=tree[tree[k].s[0]].size+1; if(!x) return k; k=tree[k].s[1]; } } } /* int findx(int k,int x) { while(1) { pushdown(k); if(x<=tree[tree[k].s[0]].size) k=tree[k].s[0]; else { x-=tree[tree[k].s[0]].size; if(x==1) { // splay(k,0); // 加不加好像都行。。。但是这个可以维护树高 return k; } k=tree[k].s[1]; --x; } } } */ inline void print(int x) { pushdown(x); if(tree[x].s[0]) print(tree[x].s[0]); if(tree[x].key!=-INF && tree[x].key!=INF) printf("%d ",tree[x].key); if(tree[x].s[1]) print(tree[x].s[1]); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) w[i+1]=i; w[1]=-INF; w[n+2]=INF; root=build(0,1,n+2); int x,y; for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); int x1=findx(root,x); int y1=findx(root,y+2); splay(x1,0); splay(y1,x1); mark[tree[tree[root].s[1]].s[0]]^=1; } print(root); } // 假如我们要在Splay中修改区间的话,可以先查找siz值为l与r+2的两个节点 // 将一个旋转到根,另一个旋转到根的左儿子上 // 则要修改的区间就是根的右孩子的左子树,直接打标记即可
7.ST标
#include<iostream> #include<cmath> #include<cstdio> using namespace std; int n,q,a[100001],L,R,f1[100001][21]; inline void ST() { int k=log(n)/log(2); // 每次并不是增加一个长度,而是使用倍增的思想,每次增加2^i个长度。 for(register int i=1;i<=n;i++) f1[i][0]=a[i]; for(register int j=1;j<=k;j++) for(register int i=1;i+(1<<j)-1<=n;i++) { f1[i][j]=max(f1[i][j-1],f1[i+(1<<(j-1))][j-1]); } } inline int RMQ(int L,int R) { int k=log(R-L+1)/log(2); return max(f1[L][k],f1[R-(1<<k)+1][k]); } int main() { scanf("%d%d",&n,&q); for(register int i=1;i<=n;i++) scanf("%d",&a[i]); ST(); for(register int i=1;i<=q;i++) { scanf("%d%d",&L,&R); printf("%d\n",RMQ(L,R)); } }