【模板】板子的集合
一个blog不能总是题解对吧,所以在这篇blog中,我会贴一些模板。顺便讲一下个人的理解。
LCA(最近公共祖先)##
1. 倍增法
首先我们要知道倍增是什么,倍增思想大概就是对一个具有单调性的操作,通过使用\(O(n \log_{2} n)\)的时间与空间对单调性进行以2的次方为基准的预处理,来使得每次操作的效率优化。
接下来讲一下LCA是什么,LCA是最近公共祖先的意思,对于OI选手来讲,LCA主要用于查询树上2点间路径的一些东西。显然,对于一棵树任意两个节点间的路径是唯一的,换而言之,每个点的祖先也具有深度上的单调性,考虑暴力求LCA,最简单的方法就是将2个点先爬到同一高度,然后一起向上爬直到相遇,这样最坏情况当树退化到链之后,每次查询时间效率为\(O(n)\)。但是,显然爬树这个过程我们可以使用倍增思想预处理出每个节点\(2^{i}\)次方的祖先,这样的话爬树过程便可以优化至\(O( \log_{2} n)\)。接下来是代码。
首先先dfs预处理出每个节点的父亲与深度。(这里使用边表存树)
inline void dfs(int x,int d,int f){
fa[x][0]=f,deep[x]=d;
for (register int i=head[x]; i; i=edge[i].next) if(edge[i].to!=f) dfs(edge[i].to,d+1,x);
}
接下来我们知道,对于一个节点,它的第\(2^{i}\)个祖先是它\(2^{i-1}\)个祖先的\(2^{i-1}\)个祖先,因此我们利用此性质花费\(O(n \log_{2} n)\)的时间利用DP预处理.
void init_LCA(){
for (int j=1; j<=lg; ++j)//lg为log2n向上取整的数
for (register int i=1; i<=n; ++i)
fa[i][j]=fa[fa[i][j-1]][j-1];
}
最后,我们每次查询先将两个节点移至同一深度,然后再进行爬树。
inline int query(int x,int y){
if (deep[x]<deep[y]) swap(x,y);
int d=deep[x]-deep[y];//d为2节点的深度差
for (register int i=0; d&&i<=lg; ++i,d>>=1) if (d&1&&fa[x][i]) x=fa[x][i];//爬至同一深度
if (!(x^y)) return x;//如果已经相遇就返回。
for (register int i=lg; i>=0; --i) if(fa[x][i]!=fa[y][i]&&fa[x][i]) x=fa[x][i],y=fa[y][i];//爬树到LCA的子节点
x=fa[x][0];y=fa[y][0];//爬到LCA
return x;
}
2. tarjan法
首先我们要知道tjlca是离线算法,是要对询问进行存储的。然后讲一下tj的思想:tarjan算法求LCA时,会后序遍历整棵树由于后序遍历的性质,当我遍历完以某节点为根一棵子树时此时所有此子树内的询问就可以被解决了(两个点都在子树内的),所以,我们考虑对于一个节点,当我遍历到它时,我便遍历所有有这个节点的询问,查看是否已经有节点被遍历过了,如果便利过了的话,显然我和它的LCA就是它当前已经被遍历的深度最小的祖先的父亲(认为现在在查询询问的点不算已经被遍历过了),这样的话,我们就可以轻松的使用并查集压缩路径以后均摊O(1)出解。由于要遍历一次整棵树,同时在遍历过程中要查看所有的询问,所以tarjanLCA的时间效率为O(n+q)。
#include<stdio.h>
#define mn 500000
struct zxy{int to,next;}edge[mn*3+5];
int h[mn+1],q[mn+1],f[mn+1],n,m,cnt,ans[mn];
bool fa[mn+1];
inline void ins(int *h,int x,int y){edge[++cnt].to=y,edge[cnt].next=h[x],h[x]=cnt;}
inline int getfa(int x){return f[x]?f[x]=getfa(f[x]):x;};
inline int in(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void tjlca(int x){
for (register int i=h[x]; i; i=edge[i].next)
tjlca(edge[i].to),f[edge[i].to]=x;
for (register int i=q[x]; i; i=edge[i].next)
if (ans[edge[i].to]) ans[edge[i].to]=getfa(ans[edge[i].to]);
else ans[edge[i].to]=x;
}
int main(){
n=in(),m=in(); int x,y;
for (int i=1; i<n; ++i) x=in(),y=in(),ins(h,x,y),fa[y]=1;
for (register int i=0; i<m; ++i) ins(q,in(),i),ins(q,in(),i);
for (register int i=1; i<=n; ++i) if (!fa[i]){tjlca(i);break;}
for (register int i=0; i<m; ++i) printf("%d\n",ans[i]);
}
3. 树剖法
不知道树剖的请自己学习有关概念orz,然后你就懂了
#include <stdio.h>
#define MN 500005
int size[MN],top[MN],son[MN],fa[MN],head[MN],deep[MN],n,q,root,cnt;
struct zxy{
int to,nxt;
}edge[MN];
inline void ins(int x,int y){edge[++cnt].to=y,edge[cnt].nxt=head[x],head[x]=cnt;}
inline void dfs1(int u,int d){
size[u]=1;deep[u]=d;
for (register int i=head[u]; i; i=edge[i].nxt){
dfs1(edge[i].to,d+1);size[u]+=size[edge[i].to];
if (size[son[u]]<size[edge[i].to]) son[u]=edge[i].to;
}
}
inline void dfs2(int u,int tp){
top[u]=tp;if (son[u]) dfs2(son[u],tp);
for (register int i=head[u]; i; i=edge[i].nxt)
if (edge[i].to!=son[u]) dfs2(edge[i].to,edge[i].to);
}
inline int query(int x,int y){
for(; top[x]^top[y]; deep[top[x]]>deep[top[y]]?x=fa[top[x]]:y=fa[top[y]]);
return deep[x]<deep[y]?x:y;
}
图论基础##
1. tarjan 求强连通分量
tarjan法求SCC,主要是在图上利用建dfs树来完成的,具体思想建议自行查阅资料。
P.S.可用于寻找无向图的桥和割点.
#include <stdio.h>
#define MN 10005
#define ME 50005
#define r register
inline int read(){
r int x; r bool f; r char c;
for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-');
for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0');
return f?-x:x;
}
int nxt[ME],to[ME],head[MN],ord[MN],low[MN],dfsn,st[MN],sn,en,scnt,v,e;
bool inz[MN];
inline void ins(int x,int y){to[++en]=y,nxt[en]=head[x],head[x]=en;}
void tj(int u){
ord[u]=low[u]=++dfsn;inz[st[sn++]=u]=1;
for (r int i=head[u]; i; i=nxt[i]){
if (!ord[to[i]]) tj(to[i]);
if (inz[to[i]]&&low[to[i]]<low[u]) low[u]=low[to[i]];
}if (ord[u]==low[u]) for(++scnt; st[sn]!=u;) inz[st[--sn]]=0;
}
int main(){
v=read(),e=read();for (r int i=1,x; i<=e; ++i) x=read(),ins(x,read());
for (r int i=1; i<=v; ++i) if (!ord[i]) tj(i); printf("%d\n",scnt);
}
2. TAG上的拓扑排序###
不断删去入度为0的点,然后删去与其相连的边,按照顺序输出即为一种拓扑序。
for (register int i=1; i<=n; ++i) if (!r[i]) que[++t]=i;
while(h<t){register int u=que[++h];printf("%d ",u);--r[to[u]];if (!r[to[u]]) que[++t]=to[u];}
3. floyd算法判联通性(bitset优化)
#include <stdio.h>
#include <bitset>
#define MN 2005
#define r register
#define getchar() (S==TT&&(TT=(S=BB)+fread(BB,1,1<<15,stdin),S==TT)?EOF:*S++)
char BB[1<<15],*S=BB,*TT=BB;
using std::bitset;
inline int read(){
r int x; r bool f; r char c;
for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-');
for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0');
return f?-x:x;
}
bitset<MN> b[MN];int n,ans;
int main(){
n=read();for (r int i=1; i<=n; ++i)
for (r int j=1; j<=n; ++j)
b[i][j]=(read()|(i==j));
for (r int k=1; k<=n; ++k)
for (r int i=1; i<=n; ++i)
if (b[i][k]) b[i]|=b[k];
for (r int i=1; i<=n; ++i) ans+=b[i].count();
printf("%d",ans);
}
4. dijsktra 算法求单源最短路###
inline void dij(int u){
for (r int i=1; i<=n; ++i) dis[i]=inf;
memset(vis,0,sizeof(vis));
dis[u]=0;tmp.no=u,tmp.val=0;heap.ins(tmp);
while(!heap.empty()){
tmp=heap.get();u=tmp.no;
if (vis[u]) continue;vis[u]=1;
for (r int i=head[u]; i; i=nxt[i])
if (!vis[to[i]]&&dis[to[i]]>dis[u]+val[i]){
dis[to[i]]=dis[u]+val[i];
tmp.no=to[i],tmp.val=dis[to[i]];heap.ins(tmp);
}
}
}
5. SPFA算法求单源最短路###
使用了循环队列与SLF优化,处理了负环判断。
inline bool SPFA(int s){
memset(inq,0,sizeof(inq));memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));dis[s]=0;que[1]=s;vis[s]=inq[s]=1;
r int h=0,t=1;while(h!=t){
r int u=que[(++h)%=MN]; vis[u]=0;
for (r int i=head[u]; i; i=nxt[i])
if (dis[to[i]]>dis[u]+val[i]){
dis[to[i]]=dis[u]+val[i];
if (!vis[to[i]]){
vis[to[i]]=1;++inq[to[i]];
if (inq[to[i]]>n) return 1;
if (dis[to[i]]<dis[que[h+1]]) que[h]=to[i],h=(h-1+MN)%MN;
else que[(++t)%=MN]=to[i];
}
}
}return 0;
}
6.Kruskal求MST###
int x[MM],y[MM],rk[MM],val[MM],n,m,fa[MN],cnt,ans,tmp;
inline int getfa(int x){return fa[x]?fa[x]=getfa(fa[x]):x;}
inline bool cmp(int x,int y){return val[x]<val[y];}
bool init(){
n=read(),m=read();
for (r int i=1; i<=m; ++i)
x[i]=read(),y[i]=read(),val[i]=read(),rk[i]=i;
std::sort(rk+1,rk+m+1,cmp);ans=inf,tmp=-1;return 1;
}
void solve(){
memset(fa,0,sizeof(fa));cnt=0;tmp=val[rk[i]];
for (r int i=1; i<=m; ++i){
r int fx=getfa(x[rk[j]]),fy=getfa(y[rk[j]]);
if (fx!=fy){
fa[fx]=fy;++cnt;
if (cnt==n-1){
ans+=val[rk[i]]
break;
}
}return ans;
}
7. 2-SAT问题
int to[MN<<2],nxt[MN<<2],head[MN<<1],n,m,avg,age[MN],en,stack[MN],top;
bool vis[MN<<1];
inline void ins(int x,int y){to[++en]=y,nxt[en]=head[x],head[x]=en;}
inline bool dfs(int u){
if (vis[u^1]) return 0;
if (vis[u]) return 1;
stack[++top]=u; vis[u]=1;
for (r int i=head[u]; i; i=nxt[i])
if (!dfs(to[i])) return 0;
return 1;
}
inline bool twoSAT(){
for (r int i=1; i<=n; ++i)
if (!vis[i<<1]&&!vis[i<<1|1]){
top=0;if (!dfs(i<<1)){
while(top) vis[stack[top--]]=0;
if (!dfs(i<<1|1)) return 0;
}
}return 1;
}
网络流##
1. dinic求最大流
首先,默认看到这里的人都知道网络的基本定义。
接下来讲一下dinic思想是什么,从源点起对图按照残量网络bfs分层,定义一个点到源点经过的最少的边数为该点的层数,然后从源点开始遍历,我们只允许流从前一层经过一条边流向后一层,最后流到汇点,这样反复操作直到汇点无法被流到,便可以找到最大流。目前可以证明的是dinic的最坏效率不到\(O(V^{2}*E)\),实际效率可能会远小于此。
附模板。
int to[MM<<1],nxt[MM<<1],cap[MM<<1],head[MN],lev[MN],que[MN],h,t,cnt=1,n,m;
inline void ins(int x,int y,int v){to[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt,cap[cnt]=v;}
inline void insw(int x,int y,int v){ins(x,y,v);ins(y,x,0);}
inline bool bfs(int s,int e){
memset(lev,-1,sizeof(lev));
h=0,t=1;que[1]=s;lev[s]=0;
while(h<t){
r int u=que[++h];
for (r int i=head[u]; i; i=nxt[i])
if (cap[i]&&lev[to[i]]==-1){
lev[to[i]]=lev[u]+1;
que[++t]=to[i];
}
}return lev[e]!=-1;
}
int dfs(int u,int f){
if (u==T) return f;
r int used=0;for (r int i=head[u]; i; i=nxt[i])
if (lev[to[i]]==lev[u]+1&&cap[i]){
r int w=dfs(to[i],min(f-used,cap[i]));
used+=w;cap[i]-=w;cap[i^1]+=w;
if (used==f) return f;
}
if (!used) lev[u]=-1;return used;
}
inline int dinic(){
r int flow=0;
while(bfs(S,T)) flow+=dfs(S,inf);
return flow;
}
2. SPFA求费用流(附单路和多路增广)###
首先,继续默认。。。。然后讲一下最小费用最大流:在保证最大流的基础之上,使得费用最低。
首先我们知道每条边有个费用,显然从源点到汇点,我们每次可以找一条费用最小的增广路,如此反复操作一定是可以保证最大流最小费用的。接下来讲一下如何实现,我们考虑每次使用SPFA找最短路(最小费用增广路),然后确认增广路增广的流量,再对残量网络和ans进行一次更新,反复操作直至没有增广路。由于SPFA的效率一直比较玄学,再加上要多次执行,这显然只能算暴力算法,但是学长说正常是不会去卡我们写SPFAcostflow的所以就假装费用流能过就行了吧23333.
然后介绍一下2个优化:
- SPFA的优化:SLF优化(small label first)优化,其实也很短的一个思想,我们每次在点入队时将当前点的dist与队首的dist做个比较,如果当前点的dist较小,就将它插到队首,优先用它更新其它点,否则就和正常的SPFA一样,虽然很简单的思想,但事实上可以减少较多的SPFA上的松弛操作。
- 第二个优化是针对SPFAcostflow的,显然SPFAcostflow是要存路径的,但是我们用边表一般是不存一条边的起点的,在费用流中我们需要建反向边用于正确增广,显然对于一条边,它的反向边的终点便是它的起点,这样子就可以削微优化一下内存,每次SPFA只需要存每个点的前驱边即可。
贴模板,上文的2个优化均已在模板中实现。
单路增广####
inline void ins(int x,int y,int val,int cost){to[++cnt]=y,nxt[cnt]=head[x],v[cnt]=val,c[cnt]=cost;head[x]=cnt;}
inline void insw(int x,int y,int v,int c){ins(x,y,v,c);ins(y,x,0,-c);}
inline bool SPFA_flow(){
memset(dis,0x3f,sizeof(dis));
r int h=0,t=1; que[1]=T;
vis[T]=1;dis[T]=0;
while(h!=t){
r int u=que[(++h)%=MN];
for (r int i=head[u]; i; i=nxt[i])
if (v[i^1]&&dis[to[i]]>dis[u]-c[i]){
r int v=to[i];dis[v]=dis[u]-c[i];
if (!vis[v]){
if (dis[v]<dis[h+1]) que[h]=v,h=(h-1+MN)%MN;
else que[(++t)%=MN]=v;
vis[v]=1;
}pree[v]=i^1;
}
vis[u]=0;
}
return dis[S]!=inf;
}
inline void costflow(){
r int cost=0;
while(SPFA_flow()){
r int mi=inf;
for (r int i=S; i!=T; i=to[pree[i]])
mi=min(mi,v[pree[i]]);
for (r int i=S; i!=T; i=to[pree[i]])
v[pree[i]]-=mi,v[pree[i]^1]+=mi;
cost+=dis[S]*mi;
}
return cost;
2017.5.31更新:在某次校内模拟赛中,由于从S开始跑SPFA,结果跑的飞慢。。。。。在学长(A队dalao@ditoly的指导下)明白了从汇点跑会优化很多的道理orz,于是更新此模板。
2017.5.31更新2:更新了多路增广模板。
多路增广####
inline void ins(int x,int y,int val,int cost){to[++cnt]=y,nxt[cnt]=head[x],v[cnt]=val,c[cnt]=cost;head[x]=cnt;}
inline void insw(int x,int y,int v,int c){ins(x,y,v,c);ins(y,x,0,-c);}
inline bool SPFA_flow(){
memset(dis,0x3f,sizeof(dis));
r int h=0,t=1; que[1]=T;
vis[T]=1;dis[T]=0;
while(h!=t){
r int u=que[(++h)%=MN];
for (r int i=head[u]; i; i=nxt[i])
if (v[i^1]&&dis[to[i]]>dis[u]+c[i^1]){
r int v=to[i];dis[v]=dis[u]+c[i^1];
if (!vis[v]){
if (dis[v]<dis[h+1]) que[h]=v,h=(h-1+MN)%MN;
else que[(++t)%=MN]=v;
vis[v]=1;
}
}
vis[u]=0;
}
memcpy(iter,head,sizeof(head));
return dis[S]!=inf;
}
int dfs(int u,int f){
vis[u]=1;if (u==T) return f;r int used=0;
for (r int &i=iter[u]; i; i=nxt[i])
if (v[i]&&!vis[to[i]]&&dis[to[i]]==dis[u]-c[i]){
r int w=dfs(to[i],min(f-used,v[i]));
used+=w;v[i]-=w,v[i^1]+=w;cost+=c[i]*w;
if (used==f) return f;
}
return used;
}
inline void costflow(){
while(SPFA_flow())do memset(vis,0,sizeof(vis)); while(dfs(S,inf));
return cost;
}
简单数据结构##
1. 循环队列
class Queue{
public:
inline void clear(){h=t=0;}
inline int get(){return queue[(++h)%=MN];}
inline void push(int k){queue[(++t)%=MN]=k;}
inline bool empty(){return h==t;}
private:
int queue[MN],h,t;
};
2. **堆(heap) **###
class heap{
public:
inline void swap(node &a,node &b){r node t=b; b=a; a=t;}
inline node get(){
r node tmp=hp[1];hp[1]=hp[sz--];
r int k=1;while((k<<1)<sz&&(hp[k<<1]<hp[k]||hp[k<<1|1]<hp[k]))
if (hp[k<<1]<hp[k<<1|1]){
swap(hp[k<<1],hp[k]);
k<<=1;
}else{
swap(hp[k<<1|1],hp[k]);k=k<<1|1;
}
if ((k<<1)==sz&&hp[k<<1]<hp[k]) swap(hp[k<<1],hp[k]);
}
inline void in(node x){
r int k=++sz;hp[k]=x;
while(k>1&&hp[k]<hp[k>>1]) swap(hp[k],hp[k>>1]),k>>=1;
}
private:
node hp[MN];
};
3. **单调队列/栈 **###
单调栈####
class Mystack{
public:
inline void clear(){top=0;}
inline int get(){return stack[top--];}
inline void ins(int v){
while(top&&v<stack[top]) --top;
stack[top]=v;
}
private:
int stack[MN],top;
};
单调队列####
class Myque{
public:
inline void clear(){h=t=0;}
inline void ins(int v){
while(t&&que[t]>v) --t;
que[++t]=v;
}
inline void get(){
return que[++h];
}
private:
que[MN],h,t;
};
中、高级数据结构##
1. ST算法解决RMQ问题
首先讲一下ST的做法:ST算法使用的是倍增的思想,我们可以预处理出对于任意一个点l,\([l,l+2^i]\)内的最值,然后每次查询就可以简单的O(1)查询\([l,l+2^k]\) , \([r-2^{k}+1,r]\)内的最值回答询问owo。
讲一下具体实现吧,首先是预处理,用f[i,j]表示从i到i+2^j内的最值。
void RMQ(){
for (int j=1; j<=lg&&(1<<j)<=n; ++j)
for (register int i=1; i+(1<<j)-1<=n; ++i){
ma[i][j]=max(ma[i][j-1],ma[i+(1<<j-1)][j-1]);
mi[i][j]=min(mi[i][j-1],mi[i+(1<<j-1)][j-1]);
}
}
然后就是查询:
inline int query(int l,int r){
int k=(int)(log(r-l+1.0)/log(2.0));
return max(ma[l][k],ma[r-(1<<k)+1][k])-min(mi[l][k],mi[r-(1<<k)+1][k]);
}
于是,我们就打完了RMQ。是不是很简单?!?
2. 线段树
class Segtree{
private:
struct node{
int mark;//val为该区间的和,mark为延迟标记
ll val;
}tree[MN<<2];
void pushdown(int t,int l,int r){//传递延迟标记
if (l==r||!tree[t].mark) return;
int x=r-l+1;
tree[t<<1].val+=(x-(x>>1))*tree[t].mark;
tree[t<<1|1].val+=(x>>1)*tree[t].mark;
tree[t<<1].mark+=tree[t].mark;
tree[t<<1|1].mark+=tree[t].mark;
tree[t].mark=0;//清空当前标记
}
public:
void init(int a,int b,int l,int r,int t,int add_val){ //进行更改 ,a,b表示当前区间的端点;l,r表示更改区间的端点;
int x=b-a+1;
if (l<=a&&r>=b){
tree[t].mark+=add_val;
tree[t].val+=x*add_val;
return;
}
pushdown(t,a,b);
int m=a+b>>1;
if (m>=l) init(a,m,l,r,t<<1,add_val);
if (m<r) init(m+1,b,l,r,t<<1|1,add_val);
tree[t].val=tree[t<<1].val+tree[t<<1|1].val;
}
ll query(int l,int r,int a,int b,int t){//查询某区间的值
if (a==l&&b==r) return tree[t].val;
pushdown(t,a,b);
int m=a+b>>1;
if (r<=m) return query(l,r,a,m,t<<1);
if (l>m) return query(l,r,m+1,b,t<<1|1);
return query(l,m,a,m,t<<1)+query(m+1,r,m+1,b,t<<1|1);
}
3. BIT树状数组
inline int lowbit(int x){
return x&(-x);
}
inline void update(int x,int add){
while(x<=n){
c[x]+=add;
x+=lowbit(x);
}
}
inline int getsum(int x){
int sum=0;
while(x){
sum+=c[x];
x-=lowbit(x);
}
return sum;
}
4. 权值线段树zkw板模板
#include <stdio.h>
#include <algorithm>
#define r register int
#define MN 500005
#define M (1<<19)
char BB[1<<26],*S=BB,c;int x;
inline int in(){
for (;(c=*S++)<'0'||c>'9';);
for (x=c-'0';(c=*S++)>='0'&&c<='9';x=(x<<3)+(x<<1)+c-'0');
return x;
}
int o[MN],v[MN],rk[MN],n[MN],q,i,t[M<<1];
bool cmp(int a,int b){return v[a]<v[b];}
inline void A(r k){for(k+=M;k;k>>=1)++t[k];}
inline int F(r k){for(r i=2;;i<<=1){if(t[i]<k)k-=t[i++];if(i>M)return i-M;}}
int main(){
fread(BB,1,1<<26,stdin);for(q=in(),i=1;i<=q;++i)o[i]=in(),v[i]=in(),n[i]=i;
std::sort(n+1,n+q+1,cmp);for(i=1;i<=q;++i)rk[n[i]]=i;
for(i=1;i<=q;++i)o[i]?(A(rk[i]),0):printf("%d\n",v[n[F(v[i])]]);
}
5. 旋转treap
class Treap{
private:
inline int Rand(){
static int x=23333;
x^=x<<13,x^=x>>17,x^=x<<5;return x;
}
struct node{
node *ls,*rs;
int val,pri,cnt,sz;
node(int val):val(val){ls=rs=NULL,cnt=sz=1,pri=Rand();}
void combine(){sz=cnt+(ls?ls->sz:0)+(rs?rs->sz:0);}
};
inline void lturn(node* &x){node *y=x->rs;x->rs=y->ls;y->ls=x;y->sz=x->sz;x->combine();x=y;}
inline void rturn(node* &x){node *y=x->ls;x->ls=y->rs;y->rs=x;y->sz=x->sz;x->combine();x=y;}
inline int Size(node *x){return x?x->sz:0;}
public:
void Insert(node* &x,int val){
if (!x){x=new node(val); return;}
if (val==x->val) x->cnt++;
else if (val>x->val){Insert(x->rs,val);if (x->rs->pri<x->pri) lturn(x);}
else {Insert(x->ls,val);if (x->ls->pri<x->pri) rturn(x);}x->combine();
}
void Delete(node *&x,int val){
if (!x) return;
if (val==x->val){
if (x->cnt>1) x->cnt--,x->sz--;
else if ((!x->ls)||(!x->rs==NULL)){
node *t=x;
if (!x->ls) x=x->rs;
else x=x->ls;
delete t;
}
else if (x->ls->pri<x->rs->pri)
rturn(x),Delete(x,val);
else lturn(x),Delete(x,val);
}
else if (val<x->val) x->sz--,Delete(x->ls,val);
else x->sz--,Delete(x->rs,val);
}
int find(node *x,int val){
if (!x) return 0;
if (val==x->sz) return Size(x->ls)+1;
if (val<x->sz) return find(x->ls,val);
else return Size(x->ls)+x->cnt+find(x->rs,val);
}
int find_rank(node *x,int k){
if (!x) return 0;
if (Size(x->ls)>=k) return find_rank(x->ls,k);
if (Size(x->ls)+x->cnt>=k) return x->val;
return find_rank(x->rs,k-Size(x->ls)-x->cnt);
}
int find_pre(node *x,int val,int ans){
if (!x) return ans;
if (val>x->val) return find_pre(x->rs,val,x->val);
return find_pre(x->ls,val,ans);
}
int find_nxt(node *x,int val,int ans){
if (!x) return ans;
if (val<x->val) return find_nxt(x->ls,val,x->val);
return find_nxt(x->rs,val,ans);
}
};
6. fhq(无旋)treap
class Treap{
private:
inline int Rand(){
static int x=23333;
return x^=x<<13,x^=x>>17,x^=x<<5;
}
struct node{
node *ls,*rs;
int val,pri,sz;
node(int val):val(val),pri(Rand()),ls(NULL),rs(NULL),sz(1){};
inline void combine(){sz=1+(ls?ls->sz:0)+(rs?rs->sz:0);}
}*root;
inline int Size(node *x){return x?x->sz:0;}
node *merge(node *a,node *b){
if (!a) return b;if (!b) return a;
if (a->pri<b->pri){
a->rs=merge(a->rs,b);
a->combine();
return a;
}else{
b->ls=merge(a,b->ls);
b->combine();
return b;
}
}
typedef std::pair<node*,node*> Droot;
Droot split(node *x,int k){
if (!x) return Droot(NULL,NULL);
r Droot y;
if (Size(x->ls)>=k){
y=split(x->ls,k);
x->ls=y.second;
x->combine();
y.second=x;
}else{
y=split(x->rs,k-Size(x->ls)-1);
x->rs=y.first;
x->combine();
y.first=x;
}return y;
}
public:
inline int findkth(int k){
r Droot x=split(root,k-1);
r Droot y=split(x.second,1);
r node *ans=y.first;
root=merge(merge(x.first,ans),y.second);
return ans->val;
}
int getkth(node *x,int val){
if (!x) return 0;
return val<x->val?getkth(x->ls,val):getkth(x->rs,val)+Size(x->ls)+1;
}
inline void Insert(int val){
r int k=getkth(root,val);
r Droot x=split(root,k);
r node *a=new node(val);
root=merge(merge(x.first,a),x.second);
}
inline void Delete(int val){
r int k=getkth(root,val);
r Droot x=split(root,k-1);
r Droot y=split(x.second,1);
r node *ans=y.first;
root=merge(x.first,y.second);
delete ans; ans=NULL;
}
inline int prefix(int val){
r int k=getkth(root,val-1);
return findkth(k);
}
inline int suffix(int val){
r int k=getkth(root,val);
return findkth(k+1);
}
};
7. 可持久化线段树(主席树)
#include <stdio.h>
#include <algorithm>
#define mid (l+r>>1)
#define MN 100005
#define MM (((1<<17)*17)<<1)
#define R register
inline int read(){
R int x; R char c; R bool f;
for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-');
for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0');
return f?-x:x;
}
int ls[MM],rs[MM],v[MM],cnt,rt[MN],val[MN],rk[MN],vrk[MN],n,m;
inline bool cmp(int a,int b){return val[a]<val[b];}
inline int copy(int old){ls[++cnt]=ls[old],rs[cnt]=rs[old],v[cnt]=v[old];return cnt;}
void modify(int val,int &x,int l,int r){
x=copy(x);++v[x];if (l==r) return;
if (val<=mid) modify(val,ls[x],l,mid);
else modify(val,rs[x],mid+1,r);
}
int query(int rk,int lq,int rq,int l,int r){
if (l==r) return l;R int tmp=v[ls[rq]]-v[ls[lq]];
if (rk<=tmp) return query(rk,ls[lq],ls[rq],l,mid);
else return query(rk-tmp,rs[lq],rs[rq],mid+1,r);
}
int main(){
n=read(),m=read();
for (R int i=1; i<=n; ++i)
val[i]=read(),rk[i]=i;
std::sort(rk+1,rk+n+1,cmp);
for (R int i=1; i<=n; ++i) vrk[rk[i]]=i;
for (R int i=1; i<=n; ++i)
rt[i]=rt[i-1],modify(vrk[i],rt[i],1,n);
for (R int i=1; i<=m; ++i){
R int l=read(),r=read(),k=read();
printf("%d\n",val[rk[query(k,rt[l-1],rt[r],1,n)]]);
}
return 0;
}
8.可持久化平衡树(可持久化无旋treap)###
/*
luogu 3835
Copyright (c) Melacau.
All rights reserved.
*/
#include <stdio.h>
#define MN 500005
#define R register
#define ll long long
#define inf 0x7fffffff
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define endfile fclose(stdin),fclose(stdout)
inline int read(){
R int x; R bool f; R char c;
for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-');
for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0');
return f?-x:x;
}
namespace Treap{
inline int Rand(){
static int seed=23333;
return seed^=seed<<13,seed^=seed>>17,seed^=seed<<5;
}
int rt[MN],cnt=1;
struct node{int ls,rs,pri,val,sz;}t[MN*50];
inline int copynode(int k){t[++cnt]=t[k];return cnt;}
inline void pushup(int k){if (k) t[k].sz=t[t[k].ls].sz+t[t[k].rs].sz+1;}
inline int newnode(int v){t[++cnt]=(node){0,0,Rand(),v,1};return cnt;}
inline int merge(int x,int y){
if (!x||!y) return x+y;
if (t[x].pri<t[y].pri){
R int k=copynode(x);
t[k].rs=merge(t[k].rs,y);
pushup(k);return k;
}else{
R int k=copynode(y);
t[k].ls=merge(x,t[k].ls);
pushup(k);return k;
}
}
inline void split(int k,int v,int &x,int &y){
if (!k) return (void)(x=y=0);
if (t[t[k].ls].sz>=v){
y=copynode(k);
split(t[y].ls,v,x,t[y].ls);
pushup(y);
}else{
x=copynode(k);
split(t[x].rs,v-t[t[k].ls].sz-1,t[x].rs,y);
pushup(x);
}
}
inline int getkth(int k,int v){
if (!k) return 0;
if (v<=t[k].val) return getkth(t[k].ls,v);
else return getkth(t[k].rs,v)+t[t[k].ls].sz+1;
}
inline int findkth(int rt,int v){
R int x,y,res;split(rt,v-1,x,y);
split(y,1,res,x);return t[res].val;
}
inline void Insert(int &rt,int v){
R int k=getkth(rt,v);
R int x,y,z;split(rt,k,x,y);
z=newnode(v);rt=merge(merge(x,z),y);
}
inline void Delete(int &rt,int v){
R int k=getkth(rt,v),x,y,z;
split(rt,k,x,y);if (findkth(y,1)!=v) return;
split(y,1,y,z);rt=merge(x,z);
}
inline int prefix(int rt,int v){
R int k=getkth(rt,v);
if (!k) return -inf;
return findkth(rt,k);
}
inline int suffix(int rt,int v){
R int k=getkth(rt,v+1),x,y;
if (k==t[rt].sz) return inf;
return findkth(rt,k+1);
}
}
using namespace Treap;
int n,v,op,x;
int main(){
n=read();for (R int i=1; i<=n; ++i){
v=read(),op=read(),x=read();
rt[i]=rt[v];if (op==1) Insert(rt[i],x);else
if (op==2) Delete(rt[i],x);else
if (op==3) printf("%d\n",getkth(rt[i],x)+1);else
if (op==4) printf("%d\n",findkth(rt[i],x));else
if (op==5) printf("%d\n",prefix(rt[i],x));else
printf("%d\n",suffix(rt[i],x));//printf("%d\n",t[rt[i]].sz);
} endfile;return 0;
}
字符串处理算法##
1. KMP算法
#include <stdio.h>
#include <string.h>
#define MN 1000005
#define r register
namespace KMP{
int f[MN],n,m;char P[MN],T[MN];
inline void getfail(){
r int i=0,j;f[0]=f[1]=0;n=strlen(P);
for (r int i=1; i<n; ++i){
j=f[i];while(j&&P[i]!=P[j]) j=f[j];
f[i+1]=P[i]==P[j]?j+1:0;
}
}
inline int getans(){
r int j=0,ans=0,m=strlen(T);
for (r int i=0; i<m; ++i){
while(j&&T[i]!=P[j]) j=f[j];
if (P[j]==T[i]) ++j;
if (j==n) ++ans;
}return ans;
}
inline int kmp(char *p,char *t){
memcpy(P,p,sizeof(p));
memcpy(T,t,sizeof(t));
getfail();return getans();
}
}
2. AC自动机
#include <string.h>
#define r register
#define MN 500005
class Queue{
public:
inline void clear(){h=t=0;}
inline int get(){return queue[(++h)%=MN];}
inline void push(int k){queue[(++t)%=MN]=k;}
inline bool empty(){return h==t;}
private:
int queue[MN],h,t;
};
class AC_automation{
public:
void init(){siz=0;rt=newnode();}
inline void ins(char *S){
r int u=rt;for (; *S; u=c[u][*S++-'a'])
if (!c[u][*S-'a']) c[u][*S-'a']=newnode(); ++val[u];
}
inline void getfail(){
q.clear();lst[rt]=f[rt]=rt;
for (r int i=0; i<26; ++i)
if (!c[rt][i]) c[rt][i]=rt;
else q.push(c[rt][i]),f[c[rt][i]]=lst[c[rt][i]]=rt;
while(!q.empty()){
r int u=q.get();
for (r int i=0; i<26; ++i){
r int v=c[u][i];
if (!v) c[u][i]=c[f[u]][i];
else{
q.push(v);f[v]=c[f[u]][i];
if (val[f[v]]) lst[v]=f[v];
else lst[v]=lst[f[v]];
}
}
}
}
inline int solve(char *S){
r int u=rt,res=0;
for (; *S; S++){
u=c[u][*S-'a'];
for (r int tmp=u; tmp!=rt; tmp=lst[tmp])
res+=val[tmp],val[tmp]=0;
}return res;
}
private:
int c[MN][26],siz,rt,val[MN],lst[MN],f[MN];Queue q;
inline int newnode(){memset(c[++siz],0,sizeof(c[siz]));val[siz]=0;return siz;}
};
3. 字符串最小表示法
求一个字符串与其字典序最小的循环同构表示,时间效率\(O(n)\)。
inline int getans(){//返回起始的位置,字符串已做复制处理。
R int i=0,j=1;
while(i<m&&j<m){
R int k=0;
while (st[i+k]==st[j+k]&&k+2<m) ++k;
if (st[i+k]>st[j+k]) i=i+k+1;
else j=j+k+1;if(i==j) ++j;
}return i<j?i:j;
}
4.后缀数组(suffix array)###
/*
poj 2774 SA
copyright (c) Melacau.
All rights reserved.
*/
#include <stdio.h>
#include <string.h>
#define R register
#define MN 200005
inline int max(int a,int b){return a>b?a:b;}
int sa[2][MN],rk[2][MN],num[MN],vl[MN],n,g,la,lb,ans,height[MN];char a[MN],b[MN];
inline void getsa(int *sa,int *rk,int *SA,int *RK,R int k){
R int i;for (i=1; i<=n; ++i) num[rk[sa[i]]]=i;//base sort 2nd key
for (i=n; i; --i) if (sa[i]>k) SA[num[rk[sa[i]-k]]--]=sa[i]-k;//get sa in [1..n-k]
for (i=n-k; ++i<=n; ) SA[num[rk[i]]--]=i;//get sa in (n-k,n]
for (i=1; i<=n; ++i) RK[SA[i]]=RK[SA[i-1]]+(rk[SA[i]]!=rk[SA[i-1]]||rk[SA[i]+k]!=rk[SA[i-1]+k]);//get all new rk
}
void build(){
R int i;for (i=1; i<=n; ++i) ++num[vl[i]];
for (i=1; i<=27; ++i) num[i]+=num[i-1];//get base sort R1
for (i=1; i<=n; ++i) sa[0][num[vl[i]]--]=i;//get sa R1
for (i=1; i<=n; ++i) rk[0][sa[0][i]]=rk[0][sa[0][i-1]]+(vl[sa[0][i]]!=vl[sa[0][i-1]]);//get all rk R1
for (i=1; rk[g][sa[g][n]]<n; i<<=1,g^=1) getsa(sa[g],rk[g],sa[g^1],rk[g^1],i);//multiply get sa
}
void get_height(){
R int i,j,k=0;for (i=1;i<=n;++i)
if (rk[g][i]!=1){
j=sa[g][rk[g][i]-1];//as h[i]>=h[i-1]-1 get pos
while(vl[i+k]==vl[j+k]) ++k;//get the length of height
height[rk[g][i]]=k;if (k) --k;//for h[i]>=h[i-1]-1 , if k > 0 then --k
}
}
int main(){
R int i;
scanf("%s",a+1);la=strlen(a+1);
scanf("%s",b+1);lb=strlen(b+1);
for (i=1; i<=la; ++i) vl[i]=a[i]-'a'+1;
vl[la+1]=27;n=la+lb+1;
for (i=la+2; i<=n; ++i) vl[i]=b[i-la-1]-'a'+1;
build();get_height();
for (R int i=2; i<=n; ++i)
if (sa[g][i]<=la&&sa[g][i-1]>la+1||sa[g][i]>la+1&&sa[g][i-1]<=la)
ans=max(ans,height[i]);
printf("%d\n",ans);
return 0;
}
数论##
1. 线性求欧拉函数(线筛)
inline void pre(){
phi[1]=1;
for (R int i=2; i<=m; ++i){
if (!b[i]){
pr[++pn]=i;
phi[i]=i-1;
}
for (R int j=1; j<=pn; ++j){
if (1ll*i*pr[j]>m) break;
b[i*pr[j]]=1;
if (i%pr[j]==0){
phi[i*pr[j]]=phi[i]*pr[j];
break;
}phi[i*pr[j]]=phi[i]*(pr[j]-1);
}
}
}
2.线性求逆元###
inv[1]=1;
for (R int i=2; i<=n; ++i)
inv[i]=(1ll*p-p/i)*inv[p%i]%p;
3.CRT(中国剩余定理)###
inline int CRT(int k){
R int res=0;
for (R int i=0; i<k; ++i){
R int tmp=(ll)inv(P/p[i]%p[i],p[i])*(P/p[i])%P*tmp%P;
res+=(ll)tmp*x[i]%P; if (res>=P) res-=P;
}return res;
}
4.bsgs(包身工树(划掉))(Baby steps Giant steps)###
std::map<int,int> mp;
inline int bsgs(int y,int z,int p){
y%=p,z%=p;if (y+z==0) return 1;
if (!y) return -1;
R int m=sqrt(p)+0.5;mp.clear();
R int tmp=0;for (R int i=0; i<=m; ++i){
if (i==0) {tmp=z%p; mp[tmp]=0; continue;}
tmp=(ll)tmp*y%p;
mp[tmp]=i;
}R int t=pw(y,m,p);tmp=1;
for (R int i=1; i<=m; ++i){
tmp=(ll)tmp*t%p;
if (mp.count(tmp)){
R ll ans=((ll)i*m)-mp[tmp];
ans=(ans%p+p)%p;
return ans;
}
}return -1;
}
5.FFT(快速傅里叶变换)(Fast Fourier Transformation)###
/*
luogu 3803 FFT
copyright (C) Melacau
All rights reserved.
*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#define R register
#define ll long long
#define MN 1<<21
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define endfile fclose(stdin),fclose(stdout)
namespace IO{
char buf[1<<15],*S,*T;
inline char gc(){
if (S==T){
T=(S=buf)+fread(buf,1,1<<15,stdin);
if (S==T) return EOF;
}return *S++;
}
inline int read(){
R int x; R bool f; R char c;
for (f=0; (c=gc())<'0'||c>'9'; f=c=='-');
for (x=c^'0'; (c=gc())>='0'&&c<='9'; x=(x<<3)+(x<<1)+(c^'0'));
return f?-x:x;
}
const int _outlim=1<<20;
char outbuf[_outlim+5];int cnt;
inline void pc(char c){
outbuf[cnt++]=c;
if (cnt==_outlim)
fwrite(outbuf,1,cnt,stdout),cnt=0;
}
inline void write(int x){
if (x>9) write(x/10);
pc(x%10^'0');
}
inline void end(){
if (cnt) fwrite(outbuf,1,cnt,stdout);endfile;
}
}
const double PI = acos(-1);
struct complex{
double re,im;
complex(double r = 0.0 , double i = 0.0):re (r), im (i) {}
inline complex operator + (const complex &b) const {return (complex){re + b.re , im + b.im};}
inline complex operator - (const complex &b) const {return (complex){re - b.re , im - b.im};}
inline complex operator * (const complex &b) const {
return (complex){re * b.re - im * b.im,b.re * im + re * b.im};
}
}a[MN],b[MN];
int n,r[MN],ans[MN],m,res,l;
inline void FFT(complex *p,int n,int d){
for (R int i = 0; i < n; ++i) if (i < r[i]) std::swap(p[i],p[r[i]]);
for (R int i = 2; i <= n; i <<= 1){
R complex w0 = complex(cos(2 * PI / i) , sin(d * 2 * PI / i)),w,u,t;
for (R int k = 0; k < n; k += i){
w = complex (1,0);
for (R int j = 0; j < i >> 1; w = w * w0 , ++j){
u = p[k + j] , t = w * p[k + j + (i>>1)];
p[k + j] = u + t;p[k + j + (i>>1)] = u - t;
}
}
}if (!(~d)) for (R int i = 0; i < n; ++i) p[i].re /= n;
}
int main(){
n = IO::read(); m = IO::read();
for (R int i = 0; i <= n; ++i) a[i] = complex(IO::read() , 0.0);
for (R int i = 0; i <= m; ++i) b[i] = complex(IO::read() , 0.0);
m += n; for (n = 1; n <= m; n <<= 1) ++l;
for (R int i = 0; i < n; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
FFT(a,n,1);FFT(b,n,1);for (R int i = 0; i < n; ++i) a[i] = a[i] * b[i];FFT(a,n,-1);
for (R int i = 0; i <= m; ++i) IO::write(a[i].re + 0.1),IO::pc(' ');IO::end();return 0;
}
6.NTT(快速数论变换)(Number Theory Transformation)
/*
luogu 3803 NTT
copyright (c) Melacau.
All rights reserved.
*/
#include <stdio.h>
#define R register
#define ll long long
#define endfile fclose(stdin),fclose(stdout)
namespace IO{
char buf[1<<15],*S,*T;
inline char gc(){
if (S==T){
T=(S=buf)+fread(buf,1,1<<15,stdin);
if (S==T) return EOF;
}return *S++;
}
inline int read(){
R int x; R bool f; R char c;
for (f=0; (c=gc())<'0'||c>'9'; f=c=='-');
for (x=c^'0'; (c=gc())>='0'&&c<='9'; x=(x<<3)+(x<<1)+(c^'0'));
return f?-x:x;
}
const int _outlim=1<<20;
char outbuf[_outlim+5];int cnt;
inline void pc(char c){
outbuf[cnt++]=c;
if (cnt==_outlim)
fwrite(outbuf,1,cnt,stdout),cnt=0;
}
inline void write(int x){
if (x>9) write(x/10);
pc(x%10^'0');
}
inline void end(){
if (cnt) fwrite(outbuf,1,cnt,stdout);endfile;
}
}
inline int pw(int x,int k,int p){
R int res=1;
for (; k; k>>=1,x=(ll)x*x%p) if (k&1) res=(ll)res*x%p;
return res;
}
#define P 998244353
#define MN (1<<21)+5
const int g=3,invg=pw(g,P-2,P);
int n,m,a[MN],b[MN],r[MN],gpow[MN],invp[MN],l;
inline void swap(int &a,int &b){a^=b,b^=a,a^=b;}
inline void mod(int &x){if (x>=P) x-=P;if (x<0) x+=P;}
inline void NTT(int *p,int n,int d){
for (R int i=0; i<n; ++i) if (i<r[i]) swap(p[i],p[r[i]]);
for (R int i=2,w0; i<=n; i<<=1){
w0=(~d)?gpow[i]:invp[i];
for (R int k=0; k<n; k+=i)
for (R int j=0,u,t,w=1; j<i>>1; ++j,w=(ll)w*w0%P){
u=p[k+j],t=(ll)w*p[k+j+(i>>1)]%P;
p[k+j]=u+t,p[k+j+(i>>1)]=u-t;
mod(p[k+j]);mod(p[k+j+(i>>1)]);
}
}if (!(~d)){R int inv=pw(n,P-2,P);for (R int i=0; i<n; ++i) p[i]=(ll)inv*p[i]%P;}
}
inline void init(){
for (R int i=1; i<=n; i<<=1) gpow[i]=pw(g,(P-1)/i,P);
for (R int i=1; i<=n; i<<=1) invp[i]=pw(invg,(P-1)/i,P);
for (R int i=0; i<n; ++i) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
}
int main(){
n=IO::read(),m=IO::read();
for (R int i=0; i<=n; ++i) a[i]=IO::read();
for (R int i=0; i<=m; ++i) b[i]=IO::read();
m+=n;for (n=1; n<=m; n<<=1) ++l;init();
NTT(a,n,1);NTT(b,n,1);for (R int i=0; i<n; ++i) a[i]=(ll)a[i]*b[i]%P;
NTT(a,n,-1);for (R int i=0; i<=m; ++i) IO::write(a[i]),IO::pc(' ');
IO::end();return 0;
}