【断更】板子题

退役选手表示在近一两年不会更新板子,可能大学会捡起来这个博客吧

离Noip也不远了,最后\(30\)天,每天早上都会敲一敲板子.

随机放出.(大家也能看一看 qwq.

并查集

#include<cstdio>
#define R register
using namespace std;
int n,m,f[10008];
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
int main()
{
    scanf("%d%d",&n,&m);
    for(R int i=1;i<=n;i++)f[i]=i;
    for(R int i=1,opt,a,b;i<=m;i++)
    {
        scanf("%d%d%d",&opt,&a,&b);
        if(opt==1)
        {
            R int fa=find(a),fb=find(b);
            f[fa]=fb;
        }
        else
        {
            R int fa=find(a),fb=find(b);
            puts(fa==fb?"Y":"N");
        }
    }
}

LIS

最长单调上升序列。

其他类型的可以改变一下符号。

#include<cstdio>
#include<algorithm>
#include<iostream>
#define R register

using namespace std;

inline void in(R int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}

int stk[100008],top,n;

int main()
{
    in(n);
    for(R int i=1,x;i<=n;i++)
    {
        in(x);
        if(stk[top]<x)stk[++top]=x;
        else
        {
            R int l=1,r=top;
            while(l<=r)
            {
                R int mid=(l+r)>>1;
                if(stk[mid]>x)r=mid-1;
                else l=mid+1;
            }
            stk[l]=x;
        }
    }
    printf("%d\n",top);
}

LCS

最长公共子序列。

如果不等,状态转移与前面存在的三种状态取\(max\).

可以滚动数组。

当然也可以树状数组优化。

这个优化必须要求:两个串均为全排列的某一种

不过的确懒得写了.

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define R register

using namespace std;

const int gz=1e4+5;

char a[gz],b[gz];
int la,lb,f[2][gz];

int main()
{
    scanf("%s",a+1);scanf("%s",b+1);
    la=strlen(a+1),lb=strlen(b+1);
    for(R int i=1;i<=la;i++)
    {
        R int op=i&1;
        for(R int j=1;j<=lb;j++)
        {
            if(a[i]==b[j])
                f[op][j]=f[op^1][j-1]+1;
            else f[op][j]=max(max(f[op^1][j],f[op][j-1]),f[op^1][j-1]);
        }
    }
    printf("%d\n",f[la&1][lb]);
}

有理数取余

给出一个有理数\(c=\frac{a}{b}\),求\(c\ \bmod 19260817\)的值。

把快读稍作模改即可.

#include<bits/stdc++.h>
#define R register
#define mod 19260817
using namespace std;
long long a,b,ans;
inline void in(long long  &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10%mod+(s-'0')%mod;s=getchar();}
    x*=f;
}
inline long long  ksm(long long x,long long p)
{
    long long res=1;
    for(;p;p>>=1,x=x*x%mod)
        if(p&1)res=res*x%mod;
    return res;
}
int main()
{
    in(a),in(b);
    if(b==0){printf("Angry!");return 0;}
    ans=a*ksm(b,mod-2);
    printf("%lld",(ans%mod+mod)%mod);
}

快速幂

注意这里要判断\(1^0 mod \ 1=0\)的情况.

#include<cstdio>
#define int long long
#define R register
using namespace std;
inline int ksm(int x,int y,int p)
{
    if(y==0)return 1;
    int res=1;
    for(;y;y>>=1,x=x*x%p)
        if(y&1)res=res*x%p;
    return res%p;
}
int x,y,p;
signed main()
{
    scanf("%lld%lld%lld",&x,&y,&p);
    printf("%lld^%lld mod %lld=%lld",x,y,p,ksm(x,y,p)%p);
}

Nim游戏

判断奇异局势.

如果每一堆石子异或起来的值不为\(0\),那么先手必胜,

如果为\(0\),那么先手必败.

#include<cstdio>
#include<algorithm>
#include<iostream>
#define R register
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int T,n;
int main()
{
    in(T);
    for(R int ans;T;T--)
    {
        in(n);ans=0;
        for(R int i=1,x;i<=n;in(x),ans^=x,i++);
        if(ans)puts("Yes");
        else puts("No");
    }
}

矩阵快速幂

难得第一次写重载,注意矩乘具有左结合右结合律的,\(ksm\)中别写反.

#include<cstdio>
#include<cctype>
#include<cstring>
#define int long long
#define R register
#define mod 1000000007
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int n,m;
struct cod
{
    int res[105][105];
    cod(){memset(res,0,sizeof res);}
    inline void nw()
    {for(R int i=1;i<=n;i++)res[i][i]=1;}
    cod friend operator *(const cod&a,const cod&b)
    {
        cod tmp;
        for(R int k=1;k<=n;k++)
            for(R int i=1;i<=n;i++)
                for(R int j=1;j<=n;j++)
                    (tmp.res[i][j]+=a.res[i][k]*b.res[k][j])%=mod;
        return tmp;
    }
}ans,x;
inline void ksm(int y)
{
    ans.nw();
    for(;y;y>>=1,x=x*x)
        if(y&1)ans=ans*x;
}
signed main()
{
    in(n),in(m);
    for(R int i=1;i<=n;i++)
        for(R int j=1;j<=n;j++)
            in(x.res[i][j]);
    ksm(m);
    for(R int i=1;i<=n;i++,puts(""))
        for(R int j=1;j<=n;j++)
            printf("%lld ",ans.res[i][j]);
}

线性基

查询最值.

开$long  long \(!!,一定要开\)long  long $!!!

PS:查询最小值的答案即为数组中最小的非零数。

查询最大值
#include<cstdio>
#include<algorithm>
#include<iostream>
#define int long long 
#define R register

using namespace std;

inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}

int p[64],ans,n;

inline void insert(R int x)
{
    for(R int i=63;~i;i--)
    {
        if(x&(1LL<<i))
        {
            if(p[i])
                x^=p[i];
            else 
            {
                p[i]=x;
                break;
            }
        }
    }
}


signed main()
{
    in(n);
    for(R int i=1,x;i<=n;i++)
        in(x),insert(x);
    for(R int i=63;~i;i--)
        if((ans^p[i])>ans)
            ans^=p[i];
    printf("%lld",ans);
}
查询第\(k\)

先将数组中的所有数变成包含这个最高位且可以通过\(Xor\)得到的最小值。

具体实现方法:

每一位向后扫,如果能变小就变小。

求解的时候就只要将\(k\)转化为二进制后,将其二进制所对应位置的数异或起来即可。

部分代码

inline int query(R int k)
{
    R int tmp[64],res=0,cnt=0;
    for(R int i=0;i<=63;i++)
    {
        for(R int j=i-1;j>=0;j--)
            if(p[i]&(1LL<<j))p[i]^=p[j];
        if(p[i])tmp[++cnt]=p[i];
    }
    for(R int i=1;i<=cnt;i++)
        if(k&(1LL<<i))res^=tmp[i];
    return res;
}

Lucas定理

实在懒得写了,就直接放一下好了,注意\(p\)必须是一个较小的质数.

#include<cstdio>
#include<cctype>
#define int long long
#define RI register int 
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int T,fac[100008];
inline int ksm(int x,int y,int p)
{
    int res=1;
    for(;y;y>>=1,x=x*x%p)
        if(y&1) res=x*res%p;
    return res;
}
inline int C(int n,int m,int p)
{
    if(m>n)return 0;
    return ((ksm(fac[n-m],p-2,p)%p*ksm(fac[m],p-2,p)%p*fac[n])%p+p)%p;
}
int Lucas(int n,int m,int p)
{
    if(m==0)return 1;
    return (Lucas(n/p,m/p,p)%p*C(n%p,m%p,p)%p+p)%p;
}
signed main()
{
    in(T);
    fac[0]=fac[1]=1;
    for(RI n,m,p;T;T--)
    {
        in(n),in(m),in(p);
        for(RI i=2;i<=p;i++)fac[i]=fac[i-1]%p*i%p;
        printf("%lld\n",(Lucas(n+m,m,p)+p)%p);
    }
}

线性筛素数

顺便写了筛欧拉函数\(\phi\)

应该没锅 qwq

#include<cstdio>
#define R register
using namespace std;
int n,m,prime[100000008],tot,phi[100000008];
bool vis[100000008];
inline void pri()
{
    vis[1]=true;
    for(R int i=2;i<=n;i++)
    {
        if(!vis[i])prime[++tot]=i,phi[i]=i-1;
        for(R int j=1;j<=tot and i*prime[j]<=n;j++)
        {
            vis[i*prime[j]]=true;
            if(i%prime[j]==0)
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            phi[i*prime[j]]=phi[i]*phi[prime[j]];
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    pri();
    for(R int x;m;m--)
    {
        scanf("%d",&x);
        puts(vis[x]==0?"Yes":"No");
    }
}

最小生成树

\(Kruskal\)做法 \(O(n\ logn)\)
#include<cstdio>
#include<algorithm> 
#define N 5000
#define R register
using namespace std;
struct cod{
    int u,v,w;
    bool operator <(const cod&a)const
    {
        return w<a.w;
    }
}edge[N*N+10];
int n,m,f[N],tot,cnt,ans;
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
int main()
{
    scanf("%d%d",&n,&m);
    for(R int i=1;i<=n;i++)f[i]=i;
    for(R int i=1;i<=m;i++)
        scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
    sort(edge+1,edge+m+1);
    for(R int i=1;i<=m;i++)
    {
        int u=edge[i].u,v=edge[i].v,w=edge[i].w;
        int fu=find(u),fv=find(v);
        if(fu==fv)continue;
        ans+=w;cnt++;f[fu]=fv;
        if(cnt==n-1)break;
    }
    if(cnt==n-1)printf("%d",ans);
    else printf("orz");
}
堆优化\(Prime\) \(O(n\ logn)\)

待补ing

最短路

\(Spfa\) \(O(ke)\)

k在大多数情况下接近2

#include<cstdio>
#include<queue>
#define N 500008
#define R  register
using namespace std;
int n,m,s,dis[N],head[N],tot;
bool vis[N];
struct cod{int u,v,w;}edge[N];
inline void add(int x,int y,int z)
{
    edge[++tot].u=head[x];
    edge[tot].v=y;
    edge[tot].w=z;
    head[x]=tot;
}
inline void spfa()
{
    for(R int i=1;i<=n;i++)dis[i]=2147483647;
    queue<int>q;
    q.push(s);vis[s]=true;dis[s]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();vis[u]=false;
        for(R int i=head[u];i;i=edge[i].u)
        {
            if(dis[edge[i].v]>dis[u]+edge[i].w)
            {
                dis[edge[i].v]=dis[u]+edge[i].w;
                if(!vis[edge[i].v])
                {
                    vis[edge[i].v]=true;
                    q.push(edge[i].v);
                }
            }
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    for(R int i=1,x,y,z;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
    }
    spfa();
    for(R int i=1;i<=n;i++)printf("%d ",dis[i]);
}
\(SPFA\)判负环(BFS)
#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
#include<algorithm>
#define R register
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
#define N 100086
#define clear(a) memset(a,0,sizeof a)
int n,m,T;
struct code{int u,v,w;}edge[N];
bool vis[N];
int head[N],tot,dis[N],cnt[N];
inline void add(int x,int y,int z)
{
    edge[++tot].u=head[x];
    edge[tot].v=y;
    edge[tot].w=z;
    head[x]=tot;
}
inline bool spfa(int now)
{
    for(R int i=1;i<=n;i++)
         vis[i]=false,dis[i]=2147483647,cnt[i]=false;
    queue<int>q;q.push(now);
    vis[now]=true;dis[now]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();vis[u]=false;
        if(cnt[u]>=n)return true;
        for(R int i=head[u];i;i=edge[i].u)
        {
            if(dis[edge[i].v]>dis[u]+edge[i].w)
            {
                dis[edge[i].v]=dis[u]+edge[i].w;
                if(!vis[edge[i].v])
                {
                    q.push(edge[i].v);
                    vis[edge[i].v]=true;
                    cnt[edge[i].v]++;
                    if(cnt[edge[i].v]>=n)return true;
                }
            }
        }
    }
    return false;
}
int main()
{
    in(T);
    while(T--)
    {
        in(n),in(m);
        tot=0;clear(head);
        for(R int  i=1,u,v,w;i<=m;i++)
        {
            in(u),in(v),in(w);
            if(w<0)add(u,v,w);
            else add(u,v,w),add(v,u,w);
        }
        puts(spfa(1)?"YE5":"N0");
    }
}
堆优化\(Dijkstra\) \(O((m+n) logn)\)

贪心策略.不能跑最长路

#include<cstdio>
#include<queue>
#define N 500008
#define R  register
using namespace std;
int n,m,s,dis[N],head[N],tot;
bool vis[N];
struct cod{int u,v,w;}edge[N];
inline void add(int x,int y,int z)
{
    edge[++tot].u=head[x];
    edge[tot].v=y;
    edge[tot].w=z;
    head[x]=tot;
}
struct hop{
    int u,d;
    bool operator<(const hop&a)
    const
    {
        return d>a.d;
    }
};
inline void dijkstra()
{
    for(R int i=1;i<=n;i++)dis[i]=2147483647;
    priority_queue<hop>q;
    q.push((hop){s,0});dis[s]=0;
    while(!q.empty())
    {
        int u=q.top().u;q.pop();
        if(vis[u])continue;
        vis[u]=true;
        for(R int i=head[u];i;i=edge[i].u)
        {
            if(dis[edge[i].v]>dis[u]+edge[i].w and !vis[edge[i].v])
            {
                dis[edge[i].v]=dis[u]+edge[i].w;
                q.push((hop){edge[i].v,dis[edge[i].v]});
            }
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    for(R int i=1,x,y,z;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
    }
    dijkstra();
    for(R int i=1;i<=n;i++)printf("%d ",dis[i]);
}

树状数组

单点修改,区间查询.

超级短的树状数组 qwq

#include<cstdio>
#include<cctype>
#define lowbit(x) x&-x
#define R register
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int n,m,tr[500008];
inline void add(int pos,int del){for(;pos<=n;pos+=lowbit(pos))tr[pos]+=del;}
inline int query(int pos){int res=0;for(;pos;pos-=lowbit(pos))res+=tr[pos];return res;}
int main()
{
    in(n),in(m);
    for(R int i=1,x;i<=n;i++)in(x),add(i,x);
    for(R int opt,l,r;m;m--)
    {
        in(opt),in(l),in(r);
        if(opt==1)add(l,r);
        else printf("%d\n",query(r)-query(l-1));
    }
}
区间修改,单点查询.

还是比较短 emmm

这里用到了差分思想,不会的可以看看我的这篇博客

#include<cstdio>
#include<cctype>
#define lowbit(x) x&-x
#define R register
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int n,m,tr[500008],last;
inline void add(int pos,int del){for(;pos<=n;pos+=lowbit(pos))tr[pos]+=del;}
inline int query(int pos){int res=0;for(;pos;pos-=lowbit(pos))res+=tr[pos];return res;}
int main()
{
    in(n),in(m);in(last);add(1,last);
    for(R int i=2,x;i<=n;i++)
    {
        in(x);
        add(i,x-last);
        last=x;
    }
    for(R int opt,l,r,x;m;m--)
    {
        in(opt);
        if(opt==1)in(l),in(r),in(x),add(l,x),add(r+1,-x);
        else in(x),printf("%d\n",query(x));
    }
}

线段树

其他类似单点修改,大同小异,这里给出区间修改,区间查询好了.

#include<cstdio>
#include<cctype>
#define ls o<<1
#define rs o<<1|1
#define int long long 
#define N 100009
#define R register
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int n,m,tr[N<<2],tg[N<<2];
inline void up(int o)
{
    tr[o]=tr[ls]+tr[rs];
}
inline void down(int o,int l,int r)
{
    if(tg[o])
    {
        tg[ls]+=tg[o];tg[rs]+=tg[o];
        int mid=(l+r)>>1;
        tr[ls]+=(mid-l+1)*tg[o];
        tr[rs]+=(r-mid)*tg[o];
        tg[o]=0;
    }
}
void build(int o,int l,int r)
{
    if(l==r)
    {
        in(tr[o]);
        return;
    }
    int mid=(l+r)>>1;
    build(ls,l,mid);build(rs,mid+1,r);
    up(o);
}
void change(int o,int l,int r,int x,int y,int z)
{
    if(x<=l and y>=r)
    {
        tr[o]+=(r-l+1)*z;
        tg[o]+=z;
        return;
    }
    down(o,l,r);
    int mid=(l+r)>>1;
    if(x<=mid)change(ls,l,mid,x,y,z);
    if(y>mid)change(rs,mid+1,r,x,y,z);
    up(o);
}
int query(int o,int l,int r,int x,int y)
{
    if(x<=l and y>=r)return tr[o];
    down(o,l,r);
    int mid=(l+r)>>1,res=0;
    if(x<=mid)res+=query(ls,l,mid,x,y);
    if(y>mid)res+=query(rs,mid+1,r,x,y);
    return res;
}
signed main()
{
    in(n),in(m);build(1,1,n);
    for(R int opt,x,y,z;m;m--)
    {
        in(opt);
        if(opt==1){in(x),in(y),in(z),change(1,1,n,x,y,z);}
        else{in(x),in(y);printf("%lld\n",query(1,1,n,x,y));}
    }
}
扫描线
一.矩形面积并

重点在于去重操作更新操作

#include<cstdio>
#include<algorithm>
#define ls o<<1
#define rs o<<1|1
#define R register
using namespace std;
struct code
{
    double l,r,h;
    int f;
    bool operator < (const code&a)const
    {
        return h<a.h;
    }
}edge[2008];
struct cod
{
    int l,r,s;
    double len;
}tr[8888];
double x[2333];
void build(int o,int l,int r)
{
    tr[o].l=l,tr[o].r=r;
    tr[o].s=0;tr[o].len=0;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
}
inline void up(int o)
{
    if(tr[o].s)
        tr[o].len=x[tr[o].r+1]-x[tr[o].l];
    else if(tr[o].l==tr[o].r)
        tr[o].len=0;
    else tr[o].len=tr[ls].len+tr[rs].len;
}
void change(int o,int l,int r,int del)
{
    if(tr[o].l==l && tr[o].r==r)
    {
        tr[o].s+=del;
        up(o);
        return;
    }
    int mid=(tr[o].l+tr[o].r)>>1;
    if(r<=mid)change(ls,l,r,del);
    else if(l>mid) change(rs,l,r,del);
    else change(ls,l,mid,del),change(rs,mid+1,r,del);
    up(o);
}
int main()
{
    int n,cas=0;
    for(;;)
    {
        scanf("%d",&n);
        if(n==0)break;
        int tot=0;
        for(R int i=1;i<=n;i++)
        {
            R double x1,x2,y1,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            edge[++tot].l=x1;edge[tot].r=x2;edge[tot].h=y1;
            edge[tot].f=1;x[tot]=x1;
            edge[++tot].l=x1;edge[tot].r=x2;edge[tot].h=y2;
            edge[tot].f=-1;x[tot]=x2;
        }
        sort(x+1,x+tot+1);
        sort(edge+1,edge+tot+1);
        int new_n=1;
        for(R int i=2;i<=tot;i++)
            if(x[new_n]!=x[i])x[++new_n]=x[i];
        build(1,1,new_n);
        double ans=0;
        for(R int i=1;i<=tot;i++)
        {
            int l=lower_bound(x+1,x+new_n+1,edge[i].l)-x;
            int r=lower_bound(x+1,x+new_n+1,edge[i].r)-x-1;
            change(1,l,r,edge[i].f);
            ans+=(edge[i+1].h-edge[i].h)*tr[1].len;
        }
        printf("Test case #%d\n",++cas);
        printf("Total explored area: %.2f\n\n",ans);
    }
}

主席树.

维护静态区间第\(k\)
#include<cstdio>
#include<iostream>
#include<algorithm>
#define N 200008
#define R register
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int n,m,a[N],b[N];
int cnt,sum[N*35],lson[N*35],rson[N*35],root[N*35];
void build(int lastroot,int &nowroot,int l,int r,int pos)
{
    nowroot=++cnt;
    sum[nowroot]=sum[lastroot]+1;
    lson[nowroot]=lson[lastroot];
    rson[nowroot]=rson[lastroot];
    if(l==r)return;
    int mid=(l+r)>>1;
    if(pos<=mid)build(lson[lastroot],lson[nowroot],l,mid,pos);
    else build(rson[lastroot],rson[nowroot],mid+1,r,pos);
}
int query(int lastroot,int nowroot,int l,int r,int pos)
{
    if(l==r)return l;
    int tmp=sum[lson[nowroot]]-sum[lson[lastroot]];
    int mid=(l+r)>>1;
    if(pos<=tmp)return query(lson[lastroot],lson[nowroot],l,mid,pos);
    else return query(rson[lastroot],rson[nowroot],mid+1,r,pos-tmp);
}
int main()
{
    in(n),in(m);
    for(R int i=1;i<=n;i++)in(a[i]),b[i]=a[i];
    sort(b+1,b+n+1);
    int new_n=1;
    for(R int i=2;i<=n;i++)
        if(b[i]!=b[new_n])b[++new_n]=b[i];
    for(R int i=1;i<=n;i++)
        build(root[i-1],root[i],1,new_n,lower_bound(b+1,b+new_n+1,a[i])-b);
    for(R int i=1,l,r,k;i<=m;i++)
    {
        in(l),in(r),in(k);
        printf("%d\n",b[query(root[l-1],root[r],1,new_n,k)]);
    }
}

ST表

这类题再不写快读,我自废双手好吧.

PS:不要用\(endl\)换行!

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cctype>
#define R  register
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int mx[100008][23],n,m;
int main()
{
    in(n),in(m);
    for(R int i=1;i<=n;i++)in(mx[i][0]);
    for(R int j=1;j<=21;j++)
        for(R int i=1;(i+(1<<j)-1)<=n;i++)
            mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]);
    for(R int l,r,k;m;m--)
    {
        in(l),in(r);
        k=log2(r-l+1);
        printf("%d\n",max(mx[l][k],mx[r-(1<<k)+1][k]));
    }
}

乘法逆元

线性求逆元

这个东西大家不应该都是临时手推嘛,为什么好多人都要背 emmm

假设我们当前枚举到了\(i\),这里\(p\)为模数

显然\(p\)可以写成这样
\[ p=k \times i+r\ \ \ (0\leq r<p) \]
则有
\[ k \times i+r \equiv 0 \ (mod \ p) \]
两边同时乘以\(r^{-1}\)\(i^{-1}\)
\[ k \times r^{-1}+i^{-1} \equiv 0 \ (mod\ p) \]
移项。
\[ i^{-1} \equiv \ -k \times r^{-1} \ \ (mod \ p) \\ \]
而我们的\(k\)又等价于\(\lfloor \frac{p}{i} \rfloor\)\(r\)等价于\(p%i\)

因此原式为
\[ i^{-1}\equiv -1\times \lfloor \frac{p}{i} \rfloor \times[p%i ]^{-1} \ (mod\ p) \]
因为可能出负数,记得每次\(+p\)\(%p\)即可

#include<cstdio>
#include<cctype>
#define int long long 
#define R register
using namespace std;
int n,p,inv[3000008];
signed main()
{
    scanf("%lld%lld",&n,&p);inv[1]=1;
    for(R int i=2;i<=n;i++)
        inv[i]=(-1*(p/i)*inv[p%i]+p)%p;
    for(R int i=1;i<=n;i++)
    printf("%lld\n",(inv[i]+p)%p);
}
快速幂求逆元

主要用到了费马小定理,这里就手推一下好了.

这是欧拉定理.
\[ a^{\phi(p)} \equiv 1 \ (mod \ p) \]
然后当\(p​\)为质数的时候,\(\phi(p)=p-1​\)

此时
\[ a^{p-1} \equiv {1}\ ( mod\ p) \]
稍作变形
\[ a^{p-2} \times a \equiv 1 \ (mod \ p) \]
此时\(a^{p-2}\)\(a^{-1}\)等价,直接求即可。

最近公共祖先(LCA)

倍增算法,直接跳即可.

#include<cstdio>
#include<cctype>
#include<iostream>
#define R register
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int n,m,s,depth[500008];
int head[500008],tot,f[500008][21];
struct cod{int u,v;}edge[1000008];
inline void add(int x,int y)
{
    edge[++tot].u=head[x];
    edge[tot].v=y;
    head[x]=tot;
}
void dfs(int u,int fa)
{
    depth[u]=depth[fa]+1;f[u][0]=fa;
    for(R int i=1;(1<<i)<=depth[u];i++)
        f[u][i]=f[f[u][i-1]][i-1];
    for(R int i=head[u];i;i=edge[i].u)
    {
        if(edge[i].v==fa)continue;
        dfs(edge[i].v,u);
    }
}
inline int lca(int x,int y)
{
    if(depth[x]>depth[y])swap(x,y);
    for(R int i=17;i>=0;i--)
        if(depth[x]+(1<<i)<=depth[y])
            y=f[y][i];
    if(x==y)return x;
    for(R int i=17;i>=0;i--)
    {
        if(f[x][i]==f[y][i])continue;
        x=f[x][i],y=f[y][i];
    }
    return f[x][0];
}
int main()
{
    in(n),in(m),in(s);
    for(R int i=1,x,y;i<n;i++)
    {
        in(x),in(y);
        add(x,y),add(y,x);
    }
    dfs(s,0);
    for(R int i=1,x,y;i<=m;i++)
    {
        in(x),in(y);
        printf("%d\n",lca(x,y));
    }
}

树链剖分

这里放一下板子好了 emm.新学的 qwq.

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define int long long
#define R register
#define ls o<<1
#define rs o<<1|1
#define clear(a,b) memset(a,b,sizeof a)
#define N 100050
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int n,m,root,p,depth[N],size[N],f[N],top[N],dfn[N],fdfn[N];
int son[N],a[N],idx;
int head[N],tot,tr[N<<2],tg[N<<2];
struct cod{int u,v;}edge[N<<1];
inline void add(int x,int y)
{
    edge[++tot].u=head[x];
    edge[tot].v=y;
    head[x]=tot;
}
inline void down(int o,int l,int r)
{
    if(tg[o])
    {
        int mid=(l+r)>>1;
        (tg[ls]+=tg[o])%=p;(tg[rs]+=tg[o])%=p;
        (tr[ls]+=(mid-l+1)*tg[o])%=p;
        (tr[rs]+=(r-mid)*tg[o])%=p;
        tg[o]=0;
    }
    return;
}
void build(int o,int l,int r)
{
    if(l==r)
    {
        tr[o]=a[fdfn[l]]%p;
        return;
    }
    int mid=(l+r)>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    (tr[o]=tr[ls]+tr[rs])%=p;
}
void change(int o,int l,int r,int x,int y,int z)
{
    if(x<=l and y>=r)
    {
        (tr[o]+=(r-l+1)*z)%=p;
        (tg[o]+=z)%=p;
        return;
    }
    down(o,l,r);
    int mid=(l+r)>>1;
    if(x<=mid)change(ls,l,mid,x,y,z);
    if(y>mid)change(rs,mid+1,r,x,y,z);
    (tr[o]=tr[ls]+tr[rs])%=p;
    return;
}
int query(int o,int l,int r,int x,int y)
{
    if(x<=l and y>=r) return tr[o];
    down(o,l,r);
    int mid=(l+r)>>1,res=0;
    if(x<=mid)res+=query(ls,l,mid,x,y);
    if(y>mid) res+=query(rs,mid+1,r,x,y);
    return res;
}
void dfs1(int u,int fa)
{
    depth[u]=depth[fa]+1;f[u]=fa;size[u]=1;
    for(R int i=head[u];i;i=edge[i].u)
    {
        if(edge[i].v==fa)continue;
        dfs1(edge[i].v,u);
        size[u]+=size[edge[i].v];
        if(son[u]==-1 or size[son[u]]<size[edge[i].v])
            son[u]=edge[i].v;
    }
}
void dfs2(int u,int t)
{
    dfn[u]=++idx;fdfn[idx]=u;top[u]=t;
    if(son[u]==-1)return;
    dfs2(son[u],t);
    for(R int i=head[u];i;i=edge[i].u)
    {
        if(dfn[edge[i].v])continue;
        dfs2(edge[i].v,edge[i].v);
    }
}
inline int tchange(int x,int y,int z)
{
    int fx=top[x],fy=top[y];
    while(fx!=fy)
    {
        if(depth[fx]>depth[fy])
        {
            change(1,1,idx,dfn[fx],dfn[x],z);
            x=f[fx];
        }
        else
        {
            change(1,1,idx,dfn[fy],dfn[y],z);
            y=f[fy];
        }
        fx=top[x],fy=top[y];
    }
    if(dfn[x]>dfn[y])swap(x,y);
    change(1,1,idx,dfn[x],dfn[y],z);
}
inline int tquery(int x,int y)
{
    int fx=top[x],fy=top[y],ans=0;
    while(fx!=fy)
    {
        if(depth[fx]>depth[fy])
        {
            (ans+=query(1,1,idx,dfn[fx],dfn[x]))%=p;
            x=f[fx];
        }
        else
        {
            (ans+=query(1,1,idx,dfn[fy],dfn[y]))%=p;
            y=f[fy];
        }
        fx=top[x],fy=top[y];
    }
    if(dfn[x]>dfn[y])swap(x,y);
    (ans+=query(1,1,idx,dfn[x],dfn[y]))%=p;
    return ans;
}
signed main()
{
    in(n),in(m),in(root),in(p);
    for(R int i=1;i<=n;i++)in(a[i]);
    clear(son,-1);
    for(R int i=1,x,y;i<n;i++)
    {
        in(x),in(y);
        add(x,y),add(y,x);
    }
    dfs1(root,-1);dfs2(root,root);
    build(1,1,idx);
    for(R int i=1,opt,l,r,z;i<=m;i++)
    {
        in(opt);
        switch(opt)
        {
            case 1:in(l),in(r),in(z),tchange(l,r,z);break;
            case 2:in(l),in(r);printf("%lld\n",(tquery(l,r)+p)%p);break;
            case 3:in(l),in(z);change(1,1,idx,dfn[l],dfn[l]+size[l]-1,z);break;
            case 4:in(l);printf("%lld\n",(query(1,1,idx,dfn[l],dfn[l]+size[l]-1)+p)%p);break;
        }
    }
}

二分图匹配

一.匈牙利算法.

记得每次\(memset\).其他没有需要特别注意的。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define R register
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int n,m,e,match[200008];
int head[200008],tot,ans;
struct cod{int u,v;}edge[200008*4];
inline void add(int x,int y)
{
    edge[++tot].u=head[x];
    edge[tot].v=y;
    head[x]=tot;
}
bool vis[200008];
bool find(int x)
{
    for(R int i=head[x];i;i=edge[i].u)
    {
        if(!vis[edge[i].v])
        {
            vis[edge[i].v]=1;
            if(!match[edge[i].v] or find(match[edge[i].v]))
            {
                match[edge[i].v]=x;
                return true;
            }
        }
    }
    return false;
}
int main()
{
    in(n),in(m),in(e);
    for(R int i=1,x,y;i<=e;i++)
    {
        in(x),in(y);
        if(y>m)continue;
        add(x,y);
    }
    for(R int i=1;i<=n;i++)
    {
        memset(vis,0,sizeof vis);
        if(find(i))ans++;
    }
    printf("%d",ans);
}
二.最大流(当前弧优化)

考虑到\(Noip​\)不考,所以没再打一次,贴代码.

网络流也忘得差不多了

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define IL inline
#define RI register int
#define N 1500000+10
IL void read(int &x){
    int f=1;x=0;char s=getchar();
    while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
    while(s<='9'&&s>='0'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
struct cod{int u,v,w;}edge[N];
int head[N],tot,s,t,depth[N],cur[N],n,m,e;
IL void add(int x,int y,int z){edge[++tot].u=head[x];edge[tot].v=y;edge[tot].w=z;head[x]=tot;}
IL bool bfs()
{
    memset(depth,0,sizeof depth);
    std::queue<int>q;
    for(RI i=0;i<=n + m + 2;i++)cur[i]=head[i];
    q.push(s);depth[s]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(RI i=head[u];i!=-1;i=edge[i].u)
        {
            if(!depth[edge[i].v]&&edge[i].w>0)
            {
                depth[edge[i].v]=depth[u]+1;
                q.push(edge[i].v);
            }
        }
    }
    if(!depth[t])return false;return true;
}
IL int dfs(int u,int dist)
{
    if(u==t || !dist)return dist;
    int di=0,f;
    for(RI i=cur[u];i!=-1;i=edge[i].u)
    {
        cur[u]=i;
        if(depth[edge[i].v]==depth[u]+1 &&( f=dfs(edge[i].v,std::min(edge[i].w,dist))))
        {
            di+=f;dist-=f;
            edge[i].w-=f;edge[i^1].w+=f;
            if(!dist)break;
        }
    }
    return di;
}
IL int dinic()
{
    int ans=0;
    while(bfs())
            ans+=dfs(s,2147483647);
    return ans;
}
int main()
{
    memset(head,-1,sizeof head);tot=-1;
    read(n),read(m),read(e);
    s=0,t=n+m+1;
    for(RI i=1,u,v,w;i<=e;i++){
        read(u),read(v);
        if(v<=m)
        {
            v+=n;
            add(u,v,1),add(v,u,0);
        }
    }
    for(RI i=1;i<=n;i++)add(s,i,1),add(i,s,0);
    for(RI i=1;i<=m;i++)add(i+n,t,1),add(t,n+i,0);
    printf("%d",dinic()); 
}

字符串

字符串哈希

这里用的是自然溢出哈希.才意识到跑这么快

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
#define R register
#define base 131
using namespace std;
int n,ans=1;
unsigned long long a[10008];
inline unsigned long long has(char *s)
{
    int len=strlen(s);
    unsigned long long res=1;
    for(R int i=0;i<len;i++)
        res=res*base+(unsigned long long)s[i];
    return res;
}
char s[1500];
int main()
{
    scanf("%d",&n);
    for(R int i=1;i<=n;i++)
        scanf("%s",s),a[i]=has(s);
    sort(a+1,a+n+1);
    for(R int i=2;i<=n;i++)
        if(a[i]!=a[i-1])ans++;
    printf("%d",ans);
}

KMP看毛片

注意存储出现位置要写成\(i-lb+1\)

#include<cstdio>
#include<cstring>
#define R register
using namespace std;
char s1[1008611],s2[1008611];
int la,lb,nex[1008611],k,loc[1008611],cnt;
int main()
{
    scanf("%s%s",s1+1,s2+1);
    la=strlen(s1+1),lb=strlen(s2+1);
    nex[1]=0;
    for(R int i=2;i<=lb;i++)
    {
        while(k and s2[k+1]!=s2[i])k=nex[k];
        if(s2[i]==s2[k+1])k++;
        nex[i]=k;
    }
    k=0;
    for(R int i=1;i<=la;i++)
    {
        while(k and s2[k+1]!=s1[i])k=nex[k];
        if(s1[i]==s2[k+1])k++;
        if(k==lb)
            loc[++cnt]=i-lb+1;
    }
    for(R int i=1;i<=cnt;i++)
        printf("%d\n",loc[i]);

    for(R int i=1;i<=lb;i++)
        printf("%d ",nex[i]);
    
}

Manacher

\(S\)中最长回文串的长度.(非插入字符版

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#define R register
using namespace std;
char ch[11000001];
char s[22000001];
int len,RL[22000001],MaxRight,center,ans;
int main()
{
    scanf("%s",ch);
    len=strlen(ch);
    for(R int i=0;i<len;i++)s[2*i+1]=ch[i];
    len=2*len+1;
    for(R int i=0;i<len;i++)
    {
        if(i<=MaxRight)
            RL[i]=min(RL[2*center-i],MaxRight-i);
        else RL[i]=1;
        while(s[i-RL[i]]==s[i+RL[i]] and i-RL[i]>=0 and i+RL[i]<len)
            RL[i]++;
        if(i+RL[i]-1>MaxRight)
            MaxRight=i+RL[i]-1,center=i;
        ans=max(ans,RL[i]-1);
    }
    printf("%d",ans);
}

AC自动机

出现的模式串的数量(就是求有多少短串在长串出现过)

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring> 
#include<iostream>
#define N 1000003
#define R register

using namespace std;

char s[N],keyword[N];

struct AC
{
   int sz,ch[N][26],cnt[N],val[N],last[N],fail[N],num;
   inline void init()
   {
       memset(ch[0],0,sizeof ch[0]);
       memset(cnt,0,sizeof cnt[0]);
       sz=1;
   }
   inline void insert(char *s)
   {
       int u=0;
       for(R int i=0;s[i];i++)
       {
           int v=s[i]-'a';
           if(!ch[u][v])
           {
               memset(ch[sz],0,sizeof ch[sz]);
               val[sz]=0;
               ch[u][v]=sz++;
           }
           u=ch[u][v];
       }
       val[u]++;
   }
   inline void fai()
   {
       fail[0]=0;
       queue<int>q;
       for(R int i=0;i<26;i++)
       {
           int u=ch[0][i];
           if(u)
           {
               fail[u]=0;
               q.push(u);
           }
       }
       while(!q.empty())
       {
           int u=q.front();q.pop();
           for(R int i=0;i<26;i++)
           {
               int v=ch[u][i];
               if(!v)
               {
                   ch[u][i]=ch[fail[u]][i];
                   continue;
               }
               q.push(v);
               fail[v]=ch[fail[u]][i];
               last[v]=val[fail[v]] ? fail[v]:last[fail[v]];
           }
       }
   }
   inline int count(char *s)
   {
       int u=0,res=0;
       for(R int i=0;s[i];i++)
       {
           int v=s[i]-'a';
           u=ch[u][v];
           if(val[u])res+=val[u],val[u]=0;
           int tmp=u;
           while(last[tmp])
           {
               tmp=last[tmp];
               if(val[tmp])res+=val[tmp],val[tmp]=0;
           }
       }
       return res;
   }
}ACs;
int main()
{
   int n;scanf("%d",&n);ACs.init();
   for(R int i=1;i<=n;i++)
   {
       cin>>keyword;
       ACs.insert(keyword);
   }
   ACs.fai();cin>>s;
   printf("%d\n",ACs.count(s));
}

输出单个模式串最多出现的次数,以及输出这个出现次数最多的模式串

#include<cstdio>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
#define R register

using namespace std;

const int maxn=1000003;

char keyword[153][71];
char s[1000006];

struct AC
{
    int ch[maxn][26],val[maxn],cnt[maxn],last[maxn],fail[maxn];
    int sz;
    inline void init()
    {
        memset(ch[0],0,sizeof ch[0]);
        memset(cnt,0,sizeof cnt);
        sz=1;
    }
    inline void insert(R char *s,R int num)
    {
        R int u=0;
        for(R int i=0;s[i];i++)
        {
            R int v=s[i]-'a';
            if(!ch[u][v])
            {
                memset(ch[sz],0,sizeof ch[sz]);
                val[sz]=0;
                ch[u][v]=sz++;
            }
            u=ch[u][v];
        }
        val[u]=num;
    }
    
    inline void fai()
    {
        memset(fail,0,sizeof fail);
        queue<int>q;fail[0]=0;
        for(R int i=0;i<26;i++)
        {
            R int u=ch[0][i];
            if(u)
            {
                fail[u]=0;
                q.push(u);
            }
        }
        while(!q.empty())
        {
            R int u=q.front();q.pop();
            for(R int i=0;i<26;i++)
            {
                R int v=ch[u][i];
                if(!v)
                {
                    ch[u][i]=ch[fail[u]][i];
                    continue;
                }
                q.push(v);
                fail[v]=ch[fail[u]][i];
                last[v]=val[fail[v]] ? fail[v]:last[fail[v]];
            }
        }
    }
    void work(R int x)
    {
        if(x)
        {
            cnt[val[x]]++;
            work(last[x]);
        }
    }
    inline void query(R char *s)
    {
        R int u=0;
        for(R int i=0;s[i];i++)
        {
            R int v=s[i]-'a';
            if(!ch[u][v])u=fail[u];
            while(u and !ch[u][v])u=fail[u];
            u=ch[u][v];
            if(val[u])work(u);
            else if(last[u])work(last[u]);
        }
    }
}ac;

int main()
{
    int T;
    scanf("%d",&T);
    while(T!=0)
    {
        R int ans=0;
        ac.init();
        for(R int  i=1;i<=T;i++)
        {
            cin>>keyword[i];
            ac.insert(keyword[i],i);
        }
        ac.fai();
        cin>>s;
        ac.query(s);
        for(R int  i=1;i<=T;i++)
        if(ac.cnt[i]>ans)ans=ac.cnt[i];
        printf("%d\n",ans);
        for(R int i=1;i<=T;i++)
            if(ac.cnt[i]==ans)
                cout<<keyword[i]<<endl;
        scanf("%d",&T);
    }
}

转载于:https://www.cnblogs.com/-guz/p/9752349.html

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值