常用结论和板子

A.数学

1.组合数的函数(快速幂)

int q_pow(int x,int n)
{
    int res=1;
    while(n)
    {
        if(n&1)
        {
            res=res*x%mod;
        }
        x=x*x%mod;
        n>>=1;
    }
    return res;
}
int C(int n,int m)
{
    if(m>n)return 0;
    return (fac[n] * q_pow(fac[m], mod - 2) % mod * q_pow(fac[n - m], mod - 2) % mod)%mod ;

}
fac[0]=1;
fac[n]=fac[n-1]*n%mod;

1.1lucas求大组合数
C(n,m) = C(n%p,m%p) * C(n/p,m/p);

ll Lucas(ll n,ll m) {
    if(m == 0) return 1;
    return C(n%mod, m%mod) * Lucas(n/mod,m/mod)%mod;
}

在这里插入图片描述

2.素数筛

bool s[100015];
void solve()
{
    s[1]=1;
    for(int i=2;i*i<=100005;i++)
    {
        if(!s[i])
        {
            for(int j=i*i;j<=100005;j+=i)s[j]=1;
        }
    }
}

3.快速乘(会爆long long时,不写高精度的方法)

ll mul(ll a,ll b){
    ll ans=0;
    while(b){
        if(b&1) ans=(ans+a)%mod;
        a=a*2%mod;
        b>>=1;
    }
    return ans;
}

4.拓展欧几里得 (求ax+by=gcd(a,b)的解 /求逆元)

LL ex_gcd(LL a,LL b,LL &x,LL &y)
{
    if(b==0)
    {
        x=1;y=0;
        return a;
    }
    LL r=ex_gcd(b,a%b,x,y);
    LL t=x;
    x=y;
    y=t-a/b*y;
    return r;
}

求逆元:A和MOD互质的时候才存在,否则不存在逆元

首先同余模定理如下:

(a+b)%c=(a%c+b%c)%c;

(ab)%c=(a%cb%c)%c;

也就是说对于取模的加减法,和乘法我们都可以运用同余模定理来进行计算,那么,对于除法我们应该怎么办呢?首先想到的就是把除法转换成乘法,然后就可以运用定理了。怎么转换呢,在普通乘法中,我们知道,除以一个数就等于乘上一个数的倒数,其实这个倒数就是我们所谓的逆元。A*(A的逆元)=单位元。在普通的乘法中
A的逆元就是它的倒数。
那么,在模n乘法中,我们应该怎么求逆元呢?我们设A的逆元为X,那么我们就可以得到(AX)%MOD=1(模n乘法的单位元也是1)。对这个式子进行变形
,就可以得到:
(A
X)%MOD=1;那么肯定存在k使得

AX=kMOD+1;

移项可得:AX-kMOD=1;

所以,当A和MOD互素时,就可以写成

AX-kMOD=gcd(A,MOD);

如果把A看做a,MOD看做b,X看做x,-k看做y的话,则上式可化为:

ax+by=gcd(a,b);

这样就可以用扩展欧几里得算法求出来x了,也就是我们要找的逆元。

5.费马小定理:
假如p是质数,且gcd(a,p)=1,那么 a^(p-1)≡1(mod p)
所以a*a^(p-2)≡1(mod p)
得出:a^(p-2)就是a的乘法逆元
利用快速幂可以计算出a^(p-2)
6.裴蜀定理
若ax+by=c,则c%(gcd(a,b))=0
7.中国剩余定理(同余方程组)

LL CRT(LL a[], LL m[], LL n)
{
    LL M = 1;
    for (int i = 0; i < n; i++) M *= m[i];
    LL ret = 0;
    for (int i = 0; i < n; i++)
    {
        LL x, y;
        LL tm = M / m[i];
        ex_gcd(tm, m[i], x, y);
        ret = (ret + tm * x * a[i]) % M;
    }
    return (ret + M) % M;
}

8.欧拉函数 对于一个正整数n,小于n且和n互质的正整数(包括1)的个数

void euler()  
{  
    E[1]=1;
    for(int i=2;i<maxn;i++){  
        if(!E[i])  
        for(int j=i;j<maxn;j+=i){  
            if(!E[j])E[j]=j;  
            E[j]=E[j]/i*(i-1);  
        }  
    }  
}

几个性质:
在这里插入图片描述
8.矩阵快速幂

#include <cstdio>
#include <iostream>
using namespace std;
long long x[999][999];
long long ans[999][999];
long long dx[999][999];
const int p=1e9+7;
void ans_cf(int n)
{
    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
      dx[i][j]=ans[i][j],ans[i][j]=0;
    
    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
      for(int k=1;k<=n;k++)
       ans[i][j]=(ans[i][j]+(x[i][k]*dx[k][j])%p)%p;
}
void x_cf(int n)
{
    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
      dx[i][j]=x[i][j],x[i][j]=0;
    
    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
      for(int k=1;k<=n;k++)
       x[i][j]=(x[i][j]+(dx[i][k]*dx[k][j])%p)%p;
}
void fast_pow(long long n,long long w)
{
    while(w)
    {
        if(w%2==1) ans_cf(n);
        w/=2;
        x_cf(n);
    }
}
int main()
{
    long long n,k;
    scanf("%lld%lld",&n,&k);
    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
      scanf("%d",&x[i][j]),ans[i][j]=x[i][j];
    fast_pow(n,k-1);
    
    for(int i=1;i<=n;i++)
    {
      for(int j=1;j<=n;j++)
       printf("%lld ",ans[i][j]);
      puts("");
    }
     
}

B.数据结构
1.链表

queue<int> q;//先假定线性队列,弹出最顶部值,根据要求插入队尾
q.pop();
if(必要なら)q.insert(x);

2.st表

int arr[max_n][m];
int ql,qr,ans;
for(int i=1;i<=n;i++)cin>>arr[i][0];
for(int i=1;i<=m;i++)
	for(int j=1;j+(1<<i)-1<=n;j++)
		arr[i][j]=max(arr[j][i-1],arr[j+(1<<(i-1))][j-1];
int k=log2(qr-ql+1);
ans=max/min(arr[l][k],arr[r-(1<<k)+1][k]);

3.并查集

int fins(int u)
{
    if(f[u]==u)return u;
    return f[u]=fins(f[u]);
}
void merg(int x,int y)
{
    x=fins(x);y=fins(y);
    if(x!=y)f[x]=y;
}

4.线段树

inline int ls(int x)
{
    return x<<1;
}
inline int rs(int x)
{
    return x<<1|1;
}
void scan()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
}
inline void push_up(int p)
{
    ans[p]=ans[ls(p)]+ans[rs(p)];
}
void build(int p,int l,int r)
{
    tag[p]=0;
    if(l==r){ans[p]=a[l];return ;}
    int mid=(l+r)>>1;
    build(ls(p),l,mid);
    build(rs(p),mid+1,r);
    push_up(p);
}
inline void f(int p,int l,int r,int k)
{
    tag[p]=tag[p]+k;
    ans[p]=ans[p]+k*(r-l+1);
}
inline void push_down(int p,int l,int r)
{
    int mid=(l+r)>>1;
    f(ls(p),l,mid,tag[p]);
    f(rs(p),mid+1,r,tag[p]);
    tag[p]=0;
}
inline void update(int nl,int nr,int l,int r,int p,int k)
{
    if(nl<=l&&r<=nr)
    {
        ans[p]+=k*(r-l+1);
        tag[p]+=k;
        return ;
    }
    push_down(p,l,r);
    int mid=(l+r)>>1;
    if(nl<=mid)update(nl,nr,l,mid,ls(p),k);
    if(nr>mid) update(nl,nr,mid+1,r,rs(p),k);
    push_up(p);
}
int query(int q_x,int q_y,int l,int r,int p)
{
    int res=0;
    if(q_x<=l&&r<=q_y)return ans[p];
    int mid=(l+r)>>1;
    push_down(p,l,r);
    if(q_x<=mid)res+=query(q_x,q_y,l,mid,ls(p));
    if(q_y>mid) res+=query(q_x,q_y,mid+1,r,rs(p));
    return res;
}

4.1加乘线段树(模块)

struct node{
    long long v, mul, add;
}st[400007];
//buildtree
void bt(int root, int l, int r){
//初始化lazytag
    st[root].mul=1;
    st[root].add=0;
    if(l==r){
        st[root].v=a[l];
    }
    else{
        int m=(l+r)/2;
        bt(root*2, l, m);
        bt(root*2+1, m+1, r);
        st[root].v=st[root*2].v+st[root*2+1].v;
    }
    st[root].v%=p;
    return ;
}
//核心代码,维护lazytag
void pushdown(int root, int l, int r){
    int m=(l+r)/2;
//根据我们规定的优先度,儿子的值=此刻儿子的值*爸爸的乘法lazytag+儿子的区间长度*爸爸的加法lazytag
    st[root*2].v=(st[root*2].v*st[root].mul+st[root].add*(m-l+1))%p;
    st[root*2+1].v=(st[root*2+1].v*st[root].mul+st[root].add*(r-m))%p;
//很好维护的lazytag
    st[root*2].mul=(st[root*2].mul*st[root].mul)%p;
    st[root*2+1].mul=(st[root*2+1].mul*st[root].mul)%p;
    st[root*2].add=(st[root*2].add*st[root].mul+st[root].add)%p;
    st[root*2+1].add=(st[root*2+1].add*st[root].mul+st[root].add)%p;
//把父节点的值初始化
    st[root].mul=1;
    st[root].add=0;
    return ;
}
//update1,乘法,stdl此刻区间的左边,stdr此刻区间的右边,l给出的左边,r给出的右边
void ud1(int root, int stdl, int stdr, int l, int r, long long k){
//假如本区间和给出的区间没有交集
    if(r<stdl || stdr<l){
        return ;
    }
//假如给出的区间包含本区间
    if(l<=stdl && stdr<=r){
        st[root].v=(st[root].v*k)%p;
        st[root].mul=(st[root].mul*k)%p;
        st[root].add=(st[root].add*k)%p;
        return ;
    }
//假如给出的区间和本区间有交集,但是也有不交叉的部分
//先传递lazytag
    pushdown(root, stdl, stdr);
    int m=(stdl+stdr)/2;
    ud1(root*2, stdl, m, l, r, k);
    ud1(root*2+1, m+1, stdr, l, r, k);
    st[root].v=(st[root*2].v+st[root*2+1].v)%p;
    return ;
}
//update2,加法,和乘法同理
void ud2(int root, int stdl, int stdr, int l, int r, long long k){
    if(r<stdl || stdr<l){
        return ;
    }
    if(l<=stdl && stdr<=r){
        st[root].add=(st[root].add+k)%p;
        st[root].v=(st[root].v+k*(stdr-stdl+1))%p;
        return ;
    }
    pushdown(root, stdl, stdr);
    int m=(stdl+stdr)/2;
    ud2(root*2, stdl, m, l, r, k);
    ud2(root*2+1, m+1, stdr, l, r, k);
    st[root].v=(st[root*2].v+st[root*2+1].v)%p;
    return ;
}

4.2动态开点

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=2e5+5;
typedef int LL;
inline LL read(){LL x=0,f=1;char ch=getchar();  while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;}
struct Tree{
   LL l,r,tag,val;
   LL lson=0,rson=0;
}tree[maxn*32];
LL tot=0;
LL newTree(LL now,LL last,LL l,LL r){
    tree[now].l=l;tree[now].r=r;
    //tree[now].tag=tree[last].tag;///继承父亲的tag
    tree[now].val=1;
    //tree[now].lson=tree[now].rson=0;
  ///  debug(now);
    return now;
}
void addtag(LL now,LL d){
	
    tree[now].tag+=d;
    tree[now].val+=d;
    //cout<<tree[now].l<<tree[now].r<<" "<<tree[now].tag<<"\n";
}
void push_down(LL now){
    if(tree[now].tag){
        LL l=tree[now].l;LL r=tree[now].r;
        LL mid=(l+r)>>1;
 
        if(!tree[now].lson) tree[now].lson=newTree(++tot,now,l,mid);
        addtag(tree[now].lson,tree[now].tag);
        if(!tree[now].rson) tree[now].rson=newTree(++tot,now,mid+1,r);
        addtag(tree[now].rson,tree[now].tag);
        tree[now].tag=0;
    }
}
void push_up(LL now){
    LL lmax=1;LL rmax=1;
    if(tree[now].lson) lmax=tree[ tree[now].lson].val;
    if(tree[now].rson) rmax=tree[ tree[now].rson].val;
    tree[now].val=max(lmax,rmax);
}
LL times=0;
void update(LL now,LL l,LL r,LL d){
     //cout<<tree[now].l<<" "<<tree[now].r<<"\n";
    //if(now==0) now=++tot;
    push_down(now);
     if(l<=tree[now].l&&r>=tree[now].r){
        addtag(now,d);
        return;
     }     
     LL mid=(tree[now].l+tree[now].r)>>1;
 //    times++;
 //    debug(mid);
  /*   if(times>=10){
        return;
     }*/
     //if(tree[now].l!=tree[now].r){
 
        if(l<=mid){
            if(!tree[now].lson) tree[now].lson=newTree(++tot,now,tree[now].l,mid);
            update(tree[now].lson,l,r,d);
        }
        if(r>mid){
            if(!tree[now].rson) tree[now].rson=newTree(++tot,now,mid+1,tree[now].r);
            update(tree[now].rson,l,r,d);
        }
     //}
     push_up(now);
}
LL query(LL now,LL l,LL r){
    //if( (!tree[now].l) && (!tree[now].r)  ) return tree[now].val;
    //cout<<tree[now].l<<" "<<tree[now].r<<" "<<tree[now].val<<"\n";
    if(now==0) return 0;
    push_down(now);
    if(l<=tree[now].l&&r>=tree[now].r){
        return tree[now].val;
    }
    LL mid=(tree[now].l+tree[now].r)>>1;
    LL ans=1;
    if(l<=mid) ans=max(ans,query(tree[now].lson,l,r));
    if(r>mid) ans=max(ans,query(tree[now].rson,l,r));
    return ans;
}
int main(void){
  ///  cin.tie(0);std::ios::sync_with_stdio(false);
    LL n,m;cin>>n>>m;
    tree[++tot].l=1;tree[tot].r=n;
    tree[tot].lson=tree[tot].rson=0;
    tree[tot].val=1;tree[tot].tag=0;///默认高度是1
    //cout<<"idiocy"<<"\n";
    for(LL i=1;i<=m;i++){
        LL op;cin>>op;
        if(op==1){
            LL l,r,x;cin>>l>>r>>x;
            update(1,l,r,x);
        }
        else if(op==2){
            LL l,r;cin>>l>>r;
            cout<<query(1,l,r)<<"\n";
        }
    }
}

5.树状数组

int n;
int a[1005],c[1005]; //对应原数组和树状数组

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

void updata(int i,int k){    //在i位置加上k
    while(i <= n){
        c[i] += k;
        i += lowbit(i);
    }
}

int getsum(int i){        //求A[1 - i]的和
    int res = 0;
    while(i > 0){
        res += c[i];
        i -= lowbit(i);
    }
    return res;
}

5.0区间查找 getsum(y)-getsum(x-1)
5.1 区间修改
差分存储 因此如果改变区间[x,y]只需要更新x-1和y

int n;
int a[1005],c[1005]; //对应原数组和树状数组

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

void updata(int i,int k){    //在i位置加上k
    while(i <= n){
        c[i] += k;
        i += lowbit(i);
    }
}

int getsum(int i){        //求A[1 - i]的和
    int res = 0;
    while(i > 0){
        res += c[i];
        i -= lowbit(i);
    }
    return res;
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		update(i,a[i]-a[i-1]);
	}
	int k,l,r,p;
	cin>>l>>r>>k;
	update(l,k);
	update(r+1,-k);
	cin>>p;
	cout<<getsum(p)<<endl;//求一点
	
}

5.1.1区间修改 求区间和
在这里插入图片描述

void updata(int i,int k){
    int x = i;    //因为x不变,所以得先保存i值
    while(i <= n){
        sum1[i] += k;
        sum2[i] += k * (x-1);
        i += lowbit(i);
    }
}

int getsum(int i){        //求前缀和
    int res = 0, x = i;
    while(i > 0){
        res += x * sum1[i] - sum2[i];
        i -= lowbit(i);
    }
    return res;
}
Σ[x,y]=getsum(y)-getsum(x-1);

C.算法
1.二分
//可取r时

while(l<=r)
mid=l+(r-l)/2; //防爆
if(?)l=mid+1;
else r=mid-1;

不可取r时

while(l<r)
mid=l+(r-l)/2;
if(?)l=mid+1;
else r=mid;

1.1 浮点数二分

while(r - l > 1e-8) {
        double mid = (l + r) / 2;
        if(?) {
            r = mid;                     
        } else {
            l = mid;
        }
    }

1.2三分

int l = 1,r = 100;
while(l < r) {
    int lmid = l + (r - l) / 3;
    int rmid = r - (r - l) / 3;
    lans = f(lmid),rans = f(rmid);
    // 求凹函数的极小值
    if(lans <= rans) r = rmid - 1;
    else l = lmid + 1;
    // 求凸函数的极大值
    if(lasn >= rans) l = lmid + 1;
    else r = rmid - 1;
}

const double EPS = 1e-9;
while(r - l < EPS) {
    double lmid = l + (r - l) / 3;
    double rmid = r - (r - l) / 3;
    lans = f(lmid),rans = f(rmid);
    // 求凹函数的极小值
    if(lans <= rans) r = rmid;
    else l = lmid;
    // 求凸函数的极大值
    if(lans >= rans) l = lmid;
    else r = rmid;


2.用于有序数组,数组的标号按升序/降序排列
其实用个vector sort一下就行了,就保留下来看看在sort里写方法的过程吧
只能用于有序,似乎没啥用

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    cin>>n;
    vector<int> a(n);
    vector<int> b(n);
    for(int i=0;i<n;i++)b[i]=i;
    for(int i=0;i<n;i++)cin>>a[i];
    sort(b.begin(),b.end(),[&](int i,int j){
         return a[i]>a[j];
         });
    for(auto &it:b)cout<<it<<" ";
    cout<<endl;
}

4.分块 莫队
总共n个元素 分为sqrt(n)块

int v[N],blocksize=0;
struct query{
	int l,r,id,bl;
}q[N];
int sum[N];
bool ans[N];
int cnt=0;
inline void add(register int x)
{
	if(++sum[v[x]]==1)
		++cnt; 
}
inline void del(register int x)
{
	if(--sum[v[x]]==0)
		--cnt;
}
inline bool cmp(register query a,register query b)
{
    return a.bl!=b.bl?a.l<b.l:((a.bl&1)?a.r<b.r:a.r>b.r);
}
int main()
{
	memset(sum,0,sizeof(sum));
	int n=read(),m=read();
	blocksize=sqrt(n);
	for(register int i=1;i<=n;++i)
		v[i]=read();
	for(register int i=1;i<=m;++i)
	{
		int l=read(),r=read();
		q[i]=(query){l,r,i,(l-1)/blocksize+1};
	}
	sort(q+1,q+m+1,cmp);
	int l=1,r=0;
	for(register int i=1;i<=m;++i)
	{
		int ll=q[i].l,rr=q[i].r;
		while(l<ll)
			del(l++);
		while(l>ll)
			add(--l);
		while(r<rr)
			add(++r);
		while(r>rr)
			del(r--);
		ans[q[i].id]=(cnt==rr-ll+1)?1:0;
	}
	for(register int i=1;i<=m;++i)
		if(ans[i])
			puts("Yes");
		else
			puts("No");
	return 0;
 } 

5.分层图dij

struct edge
{
	int u,v,w;
}e[N];
int head[N],len=0,vis[N],dis[N];
void add(int u,int v,int w)
{
	e[++len]={head[u],v,w};
	head[u]=len;
}
void dij(int s)
{
	priority_queue<pii,vector<pii>,greater<pii> > q;
	q.push(mp(0,s));
	while(!q.empty())
	{
		int i,pre=q.top().se;
		q.pop();
		if(vis[pre])
			continue;
		vis[pre]=1;
		for(i=head[pre];i;i=e[i].u)
		{
			int v=e[i].v,w=e[i].w;
			if(dis[v]>dis[pre]+w)
			{
				dis[v]=dis[pre]+w;
				q.push(mp(dis[v],v));
			}
		}
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n,m,i,k,j,s,t;
	cin>>n>>m>>k>>s>>t;
	s++;
	t++;
	for(i=1;i<=n*(k+1);i++)
		dis[i]=inf;
	dis[s]=0;
	for(i=1;i<=m;i++)
	{
		int u,v,w;
		cin>>u>>v>>w;
		u++;
		v++;
		add(u,v,w);
		add(v,u,w);
		for(j=1;j<=k;j++)
		{
			add(u+j*n,v+j*n,w);
			add(v+j*n,u+j*n,w);
			add(u+(j-1)*n,v+j*n,0);
			add(v+(j-1)*n,u+j*n,0);
		}	
	}
	dij(s);
	int ans=dis[t];
	for(i=1;i<=k;i++)
		ans=min(ans,dis[t+i*n]);		
	cout<<ans<<endl;
	return 0;
}

6.字符串的若干算法
6.1字典树

struct trie {
  int nex[100000][26], cnt;
  bool exist[100000];  // 该结点结尾的字符串是否存在

  void insert(char *s, int l) {  // 插入字符串
    int p = 0;
    for (int i = 0; i < l; i++) {
      int c = s[i] - 'a';
      if (!nex[p][c]) nex[p][c] = ++cnt;  // 如果没有,就添加结点
      p = nex[p][c];
    }
    exist[p] = 1;
  }
  bool find(char *s, int l) {  // 查找字符串
    int p = 0;
    for (int i = 0; i < l; i++) {
      int c = s[i] - 'a';
      if (!nex[p][c]) return 0;
      p = nex[p][c];
    }
    return exist[p];
  }
};

6.2 kmp

char a1[2000000],a2[2000000];
int kmp[2000000];
int main()
{
    scanf("%s%s",a1,a2);
    kmp[0]=kmp[1]=0;//前一位,两位失配了,都只可能将第一位作为新的开头
    int len1=strlen(a1),len2=strlen(a2);
    int k;
    k=0;
    for(int i=1;i<len2;i++)//自己匹配自己
    {
        while(k&&a2[i]!=a2[k])k=kmp[k];//找到最长的前后缀重叠长度
        kmp[i+1]=(a2[i]==a2[k])?++k:0;//不相等的情况,即无前缀能与后缀重叠,直接赋值位0(注意是给下一位,因为匹配的是下一位适失配的情况)
    }
    k=0;
    for(int i=0;i<len1;i++)
    {
        while(k&&a1[i]!=a2[k])k=kmp[k];//如果不匹配,则将利用kmp数组往回跳
        k+=(a1[i]==a2[k])?1:0;//如果相等了,则匹配下一位
        if(k==len2)printf("%d\n",i-len2+2);//如果已经全部匹配完毕,则输出初始位置
    }
    for(int i=1;i<=len2;i++)printf("%d ",kmp[i]);//输出f数组

6.3 ac自动机

#include<bits/stdc++.h>
#define maxn 1000001
using namespace std;
struct kkk{
    int son[26],flag,fail;
}trie[maxn];
int n,cnt;
char s[1000001];
queue<int >q;
void insert(char* s){
    int u=1,len=strlen(s);
    for(int i=0;i<len;i++){
        int v=s[i]-'a';
        if(!trie[u].son[v])trie[u].son[v]=++cnt;
        u=trie[u].son[v];
    }
    trie[u].flag++;
}
void getFail(){
    for(int i=0;i<26;i++)trie[0].son[i]=1;          //初始化0的所有儿子都是1
    q.push(1);trie[1].fail=0;               //将根压入队列
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=0;i<26;i++){              //遍历所有儿子
            int v=trie[u].son[i];           //处理u的i儿子的fail,这样就可以不用记父亲了
            int Fail=trie[u].fail;          //就是fafail,trie[Fail].son[i]就是和v值相同的点
            if(!v){trie[u].son[i]=trie[Fail].son[i];continue;}  //不存在该节点,第二种情况
            trie[v].fail=trie[Fail].son[i]; //第三种情况,直接指就可以了
            q.push(v);                      //存在实节点才压入队列
        }
    }
}
int query(char* s){
    int u=1,ans=0,len=strlen(s);
    for(int i=0;i<len;i++){
        int v=s[i]-'a';
        int k=trie[u].son[v];       //跳Fail
        while(k>1&&trie[k].flag!=-1){   //经过就不统计了
            ans+=trie[k].flag,trie[k].flag=-1;  //累加上这个位置的模式串个数,标记已经过
            k=trie[k].fail;         //继续跳Fail
        }
        u=trie[u].son[v];           //到下一个儿子
    }
    return ans;
}
int main(){
    cnt=1;            //代码实现细节,编号从1开始
        scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%s",s);
        insert(s);
    }
    getFail();
    scanf("%s",s);
    printf("%d\n",query(s));
    return 0;
}

7.高精度板子

int compare(string str1,string str2)
{
    if(str1.length()>str2.length()) return 1;
    else if(str1.length()<str2.length())  return -1;
    else return str1.compare(str2);
}
//高精度加法
//只能是两个正数相加
string add(string str1,string str2)//高精度加法
{
    string str;
    int len1=str1.length();
    int len2=str2.length();
    //前面补0,弄成长度相同
    if(len1<len2)
    {
        for(int i=1;i<=len2-len1;i++)
           str1="0"+str1;
    }
    else
    {
        for(int i=1;i<=len1-len2;i++)
           str2="0"+str2;
    }
    len1=str1.length();
    int cf=0;
    int temp;
    for(int i=len1-1;i>=0;i--)
    {
        temp=str1[i]-'0'+str2[i]-'0'+cf;
        cf=temp/10;
        temp%=10;
        str=char(temp+'0')+str;
    }
    if(cf!=0)  str=char(cf+'0')+str;
    return str;
}
//高精度减法
//只能是两个正数相减,而且要大减小
/*string sub(string str1,string str2)//高精度减法
{
    string str;
    int tmp=str1.length()-str2.length();
    int cf=0;
    for(int i=str2.length()-1;i>=0;i--)
    {
        if(str1[tmp+i]<str2[i]+cf)
        {
            str=char(str1[tmp+i]-str2[i]-cf+'0'+10)+str;
            cf=1;
        }
        else
        {
            str=char(str1[tmp+i]-str2[i]-cf+'0')+str;
            cf=0;
        }
    }
    for(int i=tmp-1;i>=0;i--)
    {
        if(str1[i]-cf>='0')
        {
            str=char(str1[i]-cf)+str;
            cf=0;
        }
        else
        {
            str=char(str1[i]-cf+10)+str;
            cf=1;
        }
    }
    str.erase(0,str.find_first_not_of('0'));//去除结果中多余的前导0
    return str;
}
//高精度乘法
//只能是两个正数相乘
string mul(string str1,string str2)
{
    string str;
    int len1=str1.length();
    int len2=str2.length();
    string tempstr;
    for(int i=len2-1;i>=0;i--)
    {
        tempstr="";
        int temp=str2[i]-'0';
        int t=0;
        int cf=0;
        if(temp!=0)
        {
            for(int j=1;j<=len2-1-i;j++)
              tempstr+="0";
            for(int j=len1-1;j>=0;j--)
            {
                t=(temp*(str1[j]-'0')+cf)%10;
                cf=(temp*(str1[j]-'0')+cf)/10;
                tempstr=char(t+'0')+tempstr;
            }
            if(cf!=0) tempstr=char(cf+'0')+tempstr;
        }
        str=add(str,tempstr);
    }
    str.erase(0,str.find_first_not_of('0'));
    return str;
}
//高精度除法
//两个正数相除,商为quotient,余数为residue
//需要高精度减法和乘法
void div(string str1,string str2,string &quotient,string &residue)
{
    quotient=residue="";//清空
    if(str2=="0")//判断除数是否为0
    {
        quotient=residue="ERROR";
        return;
    }
    if(str1=="0")//判断被除数是否为0
    {
        quotient=residue="0";
        return;
    }
    int res=compare(str1,str2);
    if(res<0)
    {
        quotient="0";
        residue=str1;
        return;
    }
    else if(res==0)
    {
        quotient="1";
        residue="0";
        return;
    }
    else
    {
        int len1=str1.length();
        int len2=str2.length();
        string tempstr;
        tempstr.append(str1,0,len2-1);
        for(int i=len2-1;i<len1;i++)
        {
            tempstr=tempstr+str1[i];
            tempstr.erase(0,tempstr.find_first_not_of('0'));
            if(tempstr.empty())
              tempstr="0";
            for(char ch='9';ch>='0';ch--)//试商
            {
                string str,tmp;
                str=str+ch;
                tmp=mul(str2,str);
                if(compare(tmp,tempstr)<=0)//试商成功
                {
                    quotient=quotient+ch;
                    tempstr=sub(tempstr,tmp);
                    break;
                }
            }
        }
        residue=tempstr;
    }
    quotient.erase(0,quotient.find_first_not_of('0'));
    if(quotient.empty()) quotient="0";
}

8.凸包

#include<bits/stdc++.h>
using namespace std;
#define N 100010
struct Node{
    double x,y;
}a[N],p[N];
int n,top=1;
double cross(Node p0,Node p1,Node p2){
    return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
double dis(Node p1,Node p2){
    return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
bool cmp(Node p1,Node p2){
    double z=cross(a[0],p1,p2);
    return (z>0||(z==0&&dis(a[0],p1)<dis(a[0],p2)));
}
void Graham(){
    int k=0;
    for(int i=0;i<n;i++)
        if(a[i].y<a[k].y||(a[i].y==a[k].y&&a[i].x<a[k].x))k=i;
    swap(a[0],a[k]);
    sort(a+1,a+n,cmp);
    p[0]=a[0];
    p[1]=a[1];
    for(int i=2;i<n;i++){
        while(cross(p[top-1],p[top],a[i])<0&&top)top--;
        p[++top]=a[i];
    }
}
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%lf%lf",&a[i].x,&a[i].y);
    Graham();
    double ans;
    for(int i=0;i<top;i++)ans+=dis(p[i],p[i+1]);
    ans+=dis(p[0],p[top]);
    printf("%.2lf",ans);
    return 0;
}

6.tarjan缩点


//cyc
#pragma GCC optimize("Ofast")
#pragma GCC target("avx,avx2,fma")
#pragma GCC optimization ("unroll-loops")
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define mst(a) memset(a,0,sizeof a)

using namespace std;
typedef pair<int,int> pii;
const int maxn=1e4+5;
int val[maxn];
int n,m;
vector<int> rec[maxn];
vector<pii> rec0;
vector<int> rec1[maxn];
vector<int> aft[maxn];
int dfn[maxn];
int vis[maxn];
int stk[maxn];
int low[maxn];
int cnt,top;
int sd[maxn];
int dp[maxn];
int ins[maxn];
void tarjan(int x)
{
    cnt++;top++;
    low[x]=dfn[x]=cnt;
    stk[top]=x;vis[x]=1;
    for(int it:rec[x]){
        if(!dfn[it]){
            tarjan(it);
            low[x]=min(low[x],low[it]);
        }
        else{
            if(vis[it]){
                low[x]=min(low[x],low[it]);
            }
        }
    }
    if(dfn[x]==low[x]){
        int y;
        while(y=stk[top--]){
            sd[y]=x;
            vis[y]=0;
            if(x==y)break;
            val[x]+=val[y];
        }
    }
}
void solve()
{
    queue<int> que;
    for(int i=1;i<=n;i++){
        if(sd[i]==i&&!ins[i]){
            que.push(i);
            dp[i]=val[i];
        }
    }
    while(!que.empty()){
        int ft=que.front();
        que.pop();
        for(int it:rec1[ft]){
            dp[it]=max(dp[it],dp[ft]+val[it]);
            ins[it]--;
            if(!ins[it])que.push(it);
        }
    }
}
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);

    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>val[i];
    }
    int u,v;
    for(int i=1;i<=m;i++){
        cin>>u>>v;
        rec[u].pb(v);
        rec0.pb({u,v});
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i])tarjan(i);
    }
    for(pii it:rec0){
        int x,y;
        x=sd[it.fi];y=sd[it.se];
        if(x!=y){
            rec1[x].pb(y);
            ins[y]++;
        }
    }
    solve();
    int ans=0;
    for(int i=1;i<=n;i++){
        ans=max(ans,dp[i]);
    }
    cout<<ans<<endl;
}

割点

void tarjan(int u,int mr){
    int rc=0;
    dfn[u]=low[u]=++cnt;
    for (int p=head[u];p;p=nxt[p]){
        int v=a[p];
        if (!dfn[v]){
            tarjan(v,mr);
            low[u]=min(low[u],low[v]);
            if (low[v]>=dfn[u]&&u!=mr) cut[u]=true;
            if (u==mr) rc++;
        }
        low[u]=min(low[u],dfn[v]);
    }    
    if (u==mr&&rc>=2) cut[mr]=true;
}

7.最近公共祖先 倍增

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=500000+2;
int n,m,s;
int k=0;
int head[maxn],d[maxn],p[maxn][21];//head数组就是链接表标配了吧?d存的是深度(deep),p[i][j]存的[i]向上走2的j次方那么长的路径
struct node{
    int v,next;
}e[maxn*2];//存树
void add(int u,int v)
{
    e[k].v=v;
    e[k].next=head[u];
    head[u]=k++;
}               //加边函数
void dfs(int u,int fa)
{
    d[u]=d[fa]+1;
    p[u][0]=fa;
    for(int i=1;(1<<i)<=d[u];i++)
        p[u][i]=p[p[u][i-1]][i-1];
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        int v=e[i].v;
        if(v!=fa)
            dfs(v,u);
    }
}                               //首先进行的预处理,将所有点的deep和p的初始值dfs出来
int lca(int a,int b)                                          //非常标准的lca查找
{
    if(d[a]>d[b])
        swap(a,b);           //保证a是在b结点上方,即a的深度小于b的深度
    for(int i=20;i>=0;i--)
        if(d[a]<=d[b]-(1<<i))
            b=p[b][i];             //先把b移到和a同一个深度
    if(a==b)
        return a;                 //特判,如果b上来和就和a一样了,那就可以直接返回答案了
    for(int i=20;i>=0;i--)
    {
        if(p[a][i]==p[b][i])
            continue;
        else
            a=p[a][i],b=p[b][i];           //A和B一起上移
    }
    return p[a][0];               找出最后a值的数字
}
int main()
{
    memset(head,-1,sizeof(head));
    int a,b;
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);                      //无向图,要加两次
    }
    dfs(s,0);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        printf("%d\n",lca(a,b));
    }
    return 0;
}

8.数位dp

求含1个数的乘积,也可以遍历从1个到二进制有效最长的长度,分别用快速幂求出每部分的值
mst成-1能过 但是mst成0就过不了
十分神奇

```cpp

//cyc
#pragma GCC optimize("Ofast")
#pragma GCC target("avx,avx2,fma")
#pragma GCC optimization ("unroll-loops")
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define mst(a) memset(a,-1,sizeof a)
#define int long long
using namespace std;
typedef pair<int,int> pii;

/*
 ____ ____   _____ .____________   _____
/_   /_   | /  |  ||   ____/_   | /  |  |
 |   ||   |/   |  ||____  \ |   |/   |  |_
 |   ||   /    ^   /       \|   /    ^   /
 |___||___\____   /______  /|___\____   |
               |__|      \/          |__|

*/
const int mod = 10000007;
int a[63];
int dp[64][64];//pos cnt

int q_pow(int x,int n)
{
    int res=1;
    while(n)
    {
        if(n&1)
        {
            res=res*x%mod;
        }
        x=x*x%mod;
        n>>=1;
    }
    return res;
}
int dfs(int pos,int now,int cnt,bool tp)//now 目前是1的位数 cnt目标1的个数
{
    if(pos==-1){
        return now==cnt;
    }
    if(!tp&&dp[pos][now]!=-1) return dp[pos][now];
    int ret=0;
    int bound=tp?a[pos]:1;
    for(int i=0;i<=bound;i++){
        ret+=dfs(pos-1,now+(i==1),cnt,tp&&(i==bound));
    }
    if(!tp)dp[pos][now]=ret;
    return ret;
}
signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int n;
    cin>>n;
    int c=0;
    while(n){
        a[c]=n&1;
        n>>=1;
        c++;
    }
   // cout<<c<<endl;
    int ans=1;
    for(int i=1;i<=c;i++){
        mst(dp);
        ans=ans*q_pow(i,dfs(c-1,0,i,1))%mod;
    }
    cout<<ans<<endl;

}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值