奇怪的模板总结(未完)

来源:互联网 发布:济宁预算软件 编辑:程序博客网 时间:2024/06/15 21:27

数据结构

记录先进后出的信息,可用于拓扑排序,Tarjan,单调栈优化,Splay中的Pushdown记录节点……

Code

void Work(){    sta[++top]=x;    x=sta[top--];}

队列

可记录先进先出的信息,用于单调队列优化,广搜等。

Code

void Work(){    queue <int> q;    q.push(x);    x=q.front();    q.pop();}

父亲的值优于子树任意一个儿子,可动态维护整个序列中的最大最小值。

Code

struct QNode{    int x;    bool operator < (const QNode &T)const{        return x<T.x;    } };void Work(){    priority_queue <QNode> q;    q.push(QNode(x));    T=q.top();    q.pop();}

可并堆(左偏树)

满足堆式存储,并且可以快速合并。

Code

struct Leftist_Tree{    int lc,rc;    int val,npl;}tr[maxn];void init(){ tr[0].npl=-1; }int Merge(int a,int b){    if(!a) return b;    if(!b) return a;    if(tr[a].val<tr[b].val) swap(a,b);    Pushdown(a);    tr[a].rc=Merge(tr[a].rc,b);    if(tr[tr[a].lc].npl<tr[tr[a].rc].npl) swap(tr[a].lc,tr[a].rc);    tr[a].npl=tr[tr[a].lc].npl+1;    return a; }  

树状数组

利用位运算动态维护前缀和,可记录一些满足区间加减法的信息。

Code

int bit[maxn];void add(int x,int val){    for(int i=x;i<=n;i+=i&-i)        bit[i]+=val;}int query(int x){    int res=0;    for(int i=x;i;i-=i&-i)        res+=bit[i];    return res;}

Splay

有双旋操作的二叉排序树,用于维护序列,维护凸包,维护元素大小关系……

Code

#define maxn 100000+5#define L(k) tr[tr[k].ch[0]]#define R(k) tr[tr[k].ch[1]]struct Splay_Tree{    int ch[2];    int key,mx,rev,tag,fa,sz;   }tr[maxn];int stack[maxn],a[maxn];int top,root,cnt;void Update(int k){    tr[k].sz=L(k).sz+R(k).sz+1;    tr[k].mx=max(L(k).mx,R(k).mx);    tr[k].mx=max(tr[k].mx,tr[k].key);}void Pushdown(int k){    if(tr[k].tag){        if(tr[k].ch[0]) L(k).tag+=tr[k].tag,L(k).mx+=tr[k].tag,L(k).key+=tr[k].tag;        if(tr[k].ch[1]) R(k).tag+=tr[k].tag,R(k).mx+=tr[k].tag,R(k).key+=tr[k].tag;    }    if(tr[k].rev){        swap(tr[k].ch[0],tr[k].ch[1]);        if(tr[k].ch[0]) L(k).rev^=1;        if(tr[k].ch[1]) R(k).rev^=1;                  }    tr[k].rev=tr[k].tag=0;}void Rotate(int &k,int d){    int t=tr[k].ch[d^1];    if(tr[t].ch[d]) tr[tr[t].ch[d]].fa=k;    tr[t].fa=tr[k].fa; tr[k].fa=t;    tr[k].ch[d^1]=tr[t].ch[d]; tr[t].ch[d]=k;    Update(k); Update(t); k=t;}void Splay(int &k,int x){    if(tr[k].ch[0]) Pushdown(tr[k].ch[0]);    if(tr[k].ch[1]) Pushdown(tr[k].ch[1]);    int d1=L(k).sz<x?1:0,t=tr[k].ch[d1];    if(!d1) x-=L(k).sz+1;    if(x){        int d2=L(t).sz<x?1:0;        if(!d2) x-=L(t).sz+1;        if(x){            Splay(tr[t].ch[d2],x);            if(d1==d2) Rotate(k,d1^1);            else Rotate(tr[k].ch[d1],d1);        }        Rotate(k,d1^1);         }}int rank(int k){    int res=L(k).sz+1;    stack[++top]=k;    while(tr[k].fa){        int p=tr[k].fa;        stack[++top]=p;        if(tr[p].ch[1]==k) res+=L(p).sz+1;        k=p;    }    return res;}void Insert(int pos,int val){    Splay(root,pos+1); Splay(tr[root].ch[1],pos+1-L(root).sz);    R(root).ch[0]=++cnt;    tr[cnt].key=tr[cnt].mx=val; tr[cnt].fa=tr[root].ch[1];    tr[cnt].sz=1;    Update(tr[root].ch[1]); Update(root);}void Delete(int l,int r){ //有必要需回收垃圾,开个stack存储剩余pos    Splay(root,l); Splay(tr[root].ch[1],r+1-L(root).sz);    R(root).ch[0]=0;    Update(tr[root].ch[1]); Update(root);}void Reverse(int l,int r){    Splay(root,l); Splay(tr[root].ch[1],r+1-L(root).sz);    int pos=R(root).ch[0];    if(!tr[pos].rev){        tr[pos].rev^=1;        swap(tr[pos].ch[0],tr[pos].ch[1]);    }    Update(tr[root].ch[1]); Update(root);}int Build(int l,int r){    if(l>r) return 0;    if(l==r){        tr[l].key=tr[l].mx=a[l]; tr[l].sz=1;        return l;    }    int k=(l+r)>>1;    tr[k].ch[0]=Build(l,k-1);    tr[k].ch[1]=Build(k+1,r);    Update(k);}

线段树

每个节点维护一个区间,每次修改最多访问logn个节点,注意lazy标记有两个或两个以上时要分清楚标记的先后顺序,科维护区间信息,维护连通性……

Code

#define maxn 200000+5typedef long long LL; struct Seg_Tree{    int l,r;    int mx,tag; LL sum;}tr[maxn<<2];int tmp[maxn];void Update(int k){    tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;    tr[k].mx=max(tr[k<<1].mx,tr[k<<1|1].mx);}void Pushdown(int k){    int sz1=tr[k<<1].r-tr[k<<1].l+1;    int sz2=tr[k<<1|1].r-tr[k<<1|1].l+1;    if(tr[k].tag){        tr[k<<1].tag+=tr[k].tag; tr[k<<1|1].tag+=tr[k].tag;        tr[k<<1].sum+=(LL)tr[k].tag*sz1; tr[k<<1|1].sum+=(LL)tr[k].tag*sz2;        tr[k<<1].mx+=tr[k].tag; tr[k<<1|1].mx+=tr[k].tag;        tr[k].tag=0;    }}void Build(int k,int l,int r){    tr[k].l=l; tr[k].r=r;    if(l==r){         tr[k].mx=tr[k].sum=tmp[l];         return;    }    int mid=(l+r)>>1;    Build(k<<1,l,mid);    Build(k<<1|1,mid+1,r);    Update(k);}void Modify(int k,int l,int r,int val){    Pushdown(k);    if(tr[k].l==l && tr[k].r==r){         tr[k].sum+=val*(r-l+1); tr[k].tag+=val; tr[k].mx+=val;        return;     }    if(tr[k<<1].r>=r) Modify(k<<1,l,r,val);    else if(tr[k<<1|1].l<=l) Modify(k<<1|1,l,r,val);    else Modify(k<<1,l,tr[k<<1].r,val),Modify(k<<1|1,tr[k<<1|1].l,r,val);    Update(k);}LL Query(int k,int l,int r){    Pushdown(k);    if(tr[k].l==l && tr[k].r==r) return tr[k].sum;    if(tr[k<<1].r>=r) return Query(k<<1,l,r);    else if(tr[k<<1|1].l<=l) return Query(k<<1|1,l,r);    else return Query(k<<1,l,tr[k<<1].r)+Query(k<<1|1,tr[k<<1|1].l,r);}

树链剖分

void dfs1(int x,int d){    sz[x]=1; dep[x]=d; son[x]=0;    for(int i=V.head[x];i!=-1;i=V.nxt[i]){        dfs1(V.v[i],d+1);        sz[x]+=sz[V.v[i]];         if(sz[V.v[i]]>sz[son[x]]) son[x]=V.v[i];    }       }void dfs2(int x,int tp){    top[x]=tp; w[x]=++cnt; ord[cnt]=x;    if(son[x]) dfs2(son[x],tp);    for(int i=V.head[x];i!=-1;i=V.nxt[i])        if(V.v[i]!=son[x])            dfs2(V.v[i],V.v[i]);        }int work(int x,int y){ // x为y父亲,否则需要每次找最深的向上回溯,知道其top相同    int res=0;    for(;top[x]!=top[y];y=fa[top[y]])        res+=Query(1,w[top[y]],w[y],p);    return res;}

主席树

如果n个节点中相邻两个节点的线段树差别不大时,我们可以考虑对每个节点都记录一个版本的线段树,相邻线段树直接的转移可以通过修改log n个节点得到。

Code

struct Seg_Tree{    int lc,rc;    LL sum;}tr[maxn<<5];int root[maxn];void Insert(int last,int &k,int l,int r,int p,int val){    k=++tot;     tr[k]=tr[last]; tr[k].sum+=val    if(l==r) return;    int mid=(l+r)>>1;    if(p<=mid) Insert(tr[last].lc,tr[k].lc,l,mid,p,val);    else Insert(tr[last].rc,tr[k].rc,mid+1,r,p,val);}LL Query(int k1,int k2,int l,int r,int p){    if(l==r) return tr[k2].sum-tr[k1].sum;    int mid=(l+r)>>1;    if(p<=mid) return Query(tr[k1].lc,tr[k2].lc,l,mid,p);    else return Query(tr[k1].rc,tr[k2].rc,mid+1,r,p)+tr[tr[k2].lc].sum-tr[tr[k1].lc].sum;}void Modify(int x,int pos,int val){    for(int i=pos;i<=n;i+=i&-i)        Insert(root[i],root[i],1,n,x,val);}

类似于树链剖分,使用Splay等数据结构动态维护树的结构,支持Link,Cut等操作。

Code

struct Splay_Tree{    int ch[2];    int val,sz,fa,pf,rev;}tr[maxn];int stack[maxn],top;int rank(int k,int &rt){    int res=tr[tr[k].ch[0]].sz+1;    stack[++top]=k;    while(tr[k].fa){        int p=tr[k].fa;        if(tr[p].ch[1]==k) res+=tr[tr[p].ch[0]].sz+1;        stack[++top]=p; k=p;    }    rt=k;    return res;}void Haha(int k){    int rt,x=rank(k,rt);    while(top) Pushdown(stack[top--]);    x=rank(k,rt); Splay(rt,x);}void Cutright(int k){    if(tr[k].ch[1]){        tr[tr[k].ch[1]].pf=k;        tr[tr[k].ch[1]].fa=0;        tr[k].ch[1]=0;        Update(k);    }}void Access(int k){    Haha(k); Cutright(k);    for(int u;u;Update(u),k=u){        u=tr[k].pf;        Haha(u); Cutright(u);        tr[u].ch[1]=k;        tr[k].pf=0; tr[k].fa=u;    }}void Beroot(int k){    Access(k); Haha(k);    tr[k].rev^=1;}int Find(int k){    Access(k); Haha(k);    while(tr[k].ch[0]) k=tr[k].ch[0];    return k;}void Link(int x,int y){    int rt1=Find(x),rt2=Find(y);    if(rt1==rt2) return;    Beroot(x); Access(y);    tr[x].pf=y;}void Cut(int x,int y){    Beroot(x); Access(y); Haha(y);    tr[y].ch[0]=tr[x].pf=tr[x].fa=0;    Update(y);}int Query(int x,int y){    Beroot(x); Access(y); Haha(y);    return tr[y].val;}

图论

二分图匹配(匈牙利算法)

Code

int link[maxn],vis[maxn];bool match(int x){    for(int i=head[x];i!=-1;i=e[i].nxt)        if(!vis[e[i].v]){            vis[e[i].v]=1;            if(link[e[i].v]==-1 || match(link[e[i].v])){                link[e[i].v]=x;                return true;            }        }    return false;}

二分图带权匹配(KM)

//------------------未完待续1s----------------------

网络流

1. 最大流(Dinic)

BFS分层图标号,DFS增广(当前弧优化记录在当前分层图到过的边)

Code

struct Edge{    int u,v,cap;    int nxt;}e[maxn<<2]; int lv[maxn],bow[maxn],head[maxn];int n,m,s,t,ans,ind;void addedge(int x,int y,int w){    e[ind]=(Edge){x,y,w,head[x]},head[x]=ind++;    e[ind]=(Edge){y,x,0,head[y]},head[y]=ind++;}bool bfs(){    queue <int> q;    memset(lv,0,sizeof(int)*(t+1));    lv[s]=1; q.push(s);    while(!q.empty()){        int x=q.front(); q.pop();        for(int i=head[x];i!=-1;i=e[i].nxt)            if(e[i].cap && !lv[e[i].v]){                lv[e[i].v]=lv[x]+1;                q.push(e[i].v);            }    }    return lv[t];}int dfs(int x,int t,int f){    if(x==t) return f;    for(int &i=bow[x];i!=-1;i=e[i].nxt)        if(e[i].cap && lv[e[i].v]>lv[x]){            int fff=dfs(e[i].v,t,min(e[i].cap,f));            if(fff){                e[i].cap-=fff;                e[i^1].cap+=fff;                return fff;            }        }       return 0;}void dinic(){    while(bfs()){        memcpy(bow,head,sizeof(int)*(t+1));        while(true){            int fff=dfs(s,t,INF);            if(fff) ans+=fff;            else break;         }    }}

有上下界:转化为费用流,把边拆成两条,一条为(up-low,1),一条为(low,-INF),只需最后加上-INF的值即可。

2.费用流(SPFA)

用SPFA增广费用最小的边,记录沿途信息。

struct Edge{    int u,v,cap,w;    int nxt;}e[maxn<<2];int pre[maxn],pre_e[maxn],dis[maxn],inq[maxn],head[maxn];int s,t,ans,ind;void addedge(int x,int y,int c,int w){    e[ind]=(Edge){x,y,c, w,head[x]},head[x]=ind++;    e[ind]=(Edge){y,x,0,-w,head[y]},head[y]=ind++;}bool SPFA(){    queue <int> q;    memset(dis,0x3f,sizeof(int)*(t+1));    dis[s]=0; q.push(s);    while(!q.empty()){        int x=q.front(); q.pop();        inq[x]=0;        for(int i=head[x];i!=-1;i=e[i].nxt)            if(e[i].cap && dis[e[i].v]>dis[x]+e[i].w){                dis[e[i].v]=dis[x]+e[i].w;                pre[e[i].v]=x; pre_e[e[i].v]=i;                if(!inq[e[i].v]){                    inq[e[i].v]=1;                    q.push(e[i].v);                }            }              }    return dis[t]!=INF;}void Costflow(){    while(SPFA()){        int fff=INF;        for(int i=t;i!=s;i=pre[i])            fff=min(fff,e[pre_e[i]].cap);        for(int i=t;i!=s;i=pre[i]){            e[pre_e[i]].cap-=fff;            e[pre_e[i]^1].cap+=fff;                 }        ans+=fff*dis[t];            }}

连通分量(Tarjan)

1.强连通分量

每次维护一个栈中标记、dfn(访问序号)、low(访问的最浅的节点)和一个栈。一个点的dfn==low从栈顶到这个点都是一个强连通分量。

Code

struct Edge{    int u,v;    int nxt;    }e[maxm];int head[maxn],stack[maxn],ins[maxn],dfn[maxn],low[maxn],d[maxn],sz[maxn];int top,n,cnt;void Tarjan(int x){    dfn[x]=low[x]=++cnt;    stack[++top]=x; ins[x]=1;    for(int i=head[x];i!=-1;i=e[i].nxt)        if(!dfn[x]){            Tarjan(e[i].v);            low[x]=min(low[x],low[e[i].v]);         }        else if(ins[e[i].v])            low[x]=min(low[x],dfn[e[i].v]);    if(dfn[x]==low[x]){        int s;        do{            s=stack[top--];            ins[s]=0;            d[s]=x; sz[x]++;        }while(s!=x);       }}

2.双连通分量

1.点双连通分量:若一个dfs树上x子节点的low[son]>=low[x],那x为割点,注意特判根节点若超过1个儿子则不为割点。
2.边双连通分量:若一个dfs树上x子节点的low[son]>low[x],则这条边为桥。统计low的同时把边入栈,当找到一个割点时把栈里的边出栈,所有边上的点在一个边双连通分量里。

Code

struct Edge{    int u,v;    int nxt;}e[maxn<<1];stack <Edge> s;bool cut[maxn];//cut[i]=true为割点int head[maxn],dfn[maxn],low[maxn],d[maxn],bcc[maxn][maxn],cutsum[maxn];int cnt,ind,tot,ans1;void Getbcc(int u,int v){//求边双连通分量    Edge E;    cut[u]=true;    bcc[++tot][0]=0;                    do{        E=s.top(); s.pop();        if(d[E.u]!=tot){            bcc[tot][++bcc[tot][0]]=E.u;            d[E.u]=tot;        }        if(d[E.v]!=tot){            bcc[tot][++bcc[tot][0]]=E.v;            d[E.v]=tot;         }    }while(E.u!=u || E.v!=v);   }void Tarjan(int t,int p){    int chsum=0;    dfn[t]=low[t]=++cnt;    for(int i=head[t];i!=-1;i=e[i].nxt)        if(p!=e[i].v){            if(!dfn[e[i].v]){                chsum++;                s.push(e[i]);                Tarjan(e[i].v,t);                low[t]=min(low[t],low[e[i].v]);                if(low[e[i].v]>=dfn[t]) Getbcc(t,e[i].v);            }            else if(dfn[e[i].v]<dfn[t]){                s.push(e[i]);                low[t]=min(low[t],dfn[e[i].v]);            }        }    if(p<0 && chsum==1) cut[t]=0;}

拓扑排序

每次把入度为0的节点放入到某数据结构中,并更新其他节点的入度。可用于判环,DP。

字符串

KMP

将匹配串与自身匹配求出next数组,再与模式串匹配。

Code

char s[maxn],t[maxn];int next[maxn];int lens,lent;void Getnext(){    next[0]=next[1]=0;    for(int i=1,now=0;i<lent;i++){        while(t[i]!=t[now] && now) now=next[now];        if(t[i]==t[now]) now++;        next[i+1]=now;      }}void KMP(){    Getnext();    for(int i=0,now=0;i<lens;i++){        while(s[i]!=t[now] && now) now=next[now];        if(s[i]==t[now]) now++;        if(now==lent) now=next[now];            }}

Trie树

后缀数组

维护每一个后缀的“字典序”排名和与前一个排名的后缀的LCP的长度。
每一个后缀的前缀都是原本字符串的一个子串。

Code

char ch[maxn];int sa[maxn],t1[maxn],t2[maxn],c[maxn];int rank[maxn],height[maxn];int len;bool cmp(int *r,int a,int b,int k){    return r[a]==r[b] && r[a+k]==r[b+k];}void DA(int n,int m){    int *x=t1,*y=t2;    for(int i=0;i<m;i++) c[i]=0;    for(int i=0;i<n;i++) c[x[i]=ch[i]]++;    for(int i=1;i<m;i++) c[i]+=c[i-1];    for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;    for(int k=1;k<=n;k<<=1){        int p1=0,p2=1;        for(int i=n-k;i<n;i++) y[p1++]=i;        for(int i=0;i<n;i++) if(sa[i]>=k) y[p1++]=sa[i]-k;        for(int i=1;i<m;i++) c[i]=0;        for(int i=0;i<n;i++) c[x[y[i]]]++;        for(int i=1;i<m;i++) c[i]+=c[i-1];        for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];        swap(x,y);        x[sa[0]]=0;        for(int i=1;i<n;i++)            x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p2-1:p2++;        if(p2>=n) return;        m=p2;    }}void Getheight(int n){    for(int i=0;i<=n;i++) rank[sa[i]]=i;    for(int k=0,i=0;i<n;i++){        if(k) k--;        int j=sa[rank[i]-1];        while(ch[i+k]==ch[j+k]) k++;        height[rank[i]]=k;    }}int main(){    scanf("%s",ch);    len=strlen(ch);    DA(len+1,128);    Getheight(len);    return 0;   }

后缀自动机

#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#define maxn 20000+5using namespace std;struct SAM{    int am[maxn][26],mx[maxn],fa[maxn];    int last,cnt;    SAM(){ last=cnt=1; }    void extend(int c){        int p=last,np=++cnt; last=np;        mx[np]=mx[p]+1;        for(;p && !am[p][c];p=fa[p]) am[p][c]=np;        if(!p) fa[np]=1;        else{            int q=am[p][c];            if(mx[q]==mx[p]+1) fa[np]=q;            else{                int nq=++cnt;                memcpy(am[nq],am[q],sizeof(am[q]));                mx[nq]=mx[p]+1;                fa[nq]=fa[q];                fa[q]=fa[np]=nq;                for(;p && am[p][c]==q;p=fa[p]) am[p][c]=nq;            }        }    }}sam;char s[maxn];int len;int main(){    scanf("%s",s+1);    len=strlen(s+1);    for(int i=1;i<=len;i++)        sam.extend(s[i]-'A');    return 0;   }

字符串哈希

//------------------未完待续1s----------------------

马拉车

//------------------未完待续1s----------------------

数论

线性规划(单纯形法)

#include<cmath>#include<cstdio>#include<climits>#include<cstring>#include<cstdlib>#include<algorithm>#define maxn 1000+5#define maxm 10000+5#define INF (INT_MAX-1)using namespace std;const double eps=1e-7;double a[maxm][maxn],b[maxm],c[maxn];double ans;int n,m;void pivot(int l,int e){    b[l]/=a[l][e];    a[l][e]=1.0/a[l][e];    for(int i=1;i<=n;i++)        if(i!=e) a[l][i]*=a[l][e];    //处理原等式    for(int i=1;i<=m;i++)        if(i!=l && fabs(a[i][e])>eps){            b[i]-=a[i][e]*b[l];            for(int j=1;j<=n;j++)                if(j!=e) a[i][j]-=a[l][j]*a[i][e];                              a[i][e]=-a[l][e]*a[i][e];        }    //代入其余等式    ans+=b[l]*c[e];     for(int i=1;i<=n;i++) if(i!=e) c[i]-=c[e]*a[l][i];    c[e]=-c[e]*a[l][e];    //代入目标函数}double Simplex(){    while(true){        int e=0,l=0;        double tmp=INF;        for(int i=1;i<=n;i++)            if(c[i]>eps && c[i]>c[e]) e=i;        //贪心加一波ans,快了两三倍        if(e==0) return ans;        for(int i=1;i<=m;i++)            if(a[i][e]>eps && tmp>b[i]/a[i][e]){                l=i; tmp=b[i]/a[i][e];            }        if(tmp==INF) return INF;                pivot(l,e);    }}int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++)        scanf("%lf",&c[i]);    for(int l,r,i=1;i<=m;i++){        scanf("%d%d",&l,&r);        for(int j=l;j<=r;j++) a[i][j]=1;        scanf("%lf",&b[i]);    }    printf("%d",(int)(Simplex()+0.5));      return 0;}
0 0