模板(未完结)

模板

存放一些学过的模板,未完结。


  • 离散化
//方法一:STL

//a[]为被离散化数组
int a[maxn],t[maxn];
for(int i=1;i<=n;i++)a[i]=read(),t[i]=a[i];
sort(t+1,t+n+1);
int m=unique(t+1,t+n+1)-t-1;//注意要-1;
for(int i=1;i<=n;i++)a[i]=lower_bound(t+1,t+m+1,a[i])-t;//而这里,是不需要-1的;另,n不是m
//方法二:数组离散化

int cmp(Node a,Node b){return a.v<b.v;}//比较函数

int rnk[maxn];
struct Node{v,id}a[maxn];
for(int i=1;i<=n;i++)a[i].v=read(),a[i].id=i;
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)rnk[a[i].id]=i;
  • 线段树

//pushup

void pu(int now,int l,int r)
{
	sum[now]=sum[ls]+sum[rs];
	return ;
}
//pushdown

void pd(int now,int l,int r)
{
	sum[ls]+=(mid-l+1)*lazy[now];sum[rs]+=(r-mid)*lazy[now];
	lazy[ls]+=lazy[now];lazy[rs]+=lazy[now];
	lazy[now]=0;
	return ;
}
//buildtree

void build(int now,int l,int r)
{
	if(l==r){sum[now]=read();return ;}
	build(ls,l,mid);
	build(rs,mid+1,r);
	pu(now,l,r);
	return ;
}

//insert

void ins(int now,int l,int r,int L,int R,int v)
{
	if(L<=l&&r<=R){sum[now]+=(r-l+1)*v;lazy[now]+=v;return ;}
	if(lazy[now])pd(now,l,r);
	if(L<=mid)ins(ls,l,mid,L,R,v);
	if(R>=mid+1)ins(rs,mid+1,r,L,R,v);
	pu(now,l,r);
}
//query

int query(int now,int l,int r,int L,int R)
{
	if(L<=l&&r<=R){return sum[now];}
    if(lazy[now])pd(now,l,r);
    int ret=0;
    if(L<=mid)ret+=query(ls,l,mid,L,R);
    if(R>=mid+1)ret+=query(rs,mid+1,r,L,R);
    return ret;
}
  • 可持久化线段树(主席树)
//1.建树

void build(int &now,int l,int r)
{
	now=++tot;
	if(l==r){return ;}
	build(tree[now].l,l,mid);
	build(tree[now].r,mid+1,r);
	return ;
}


//2.修改

void update(int &now,int pre,int l,int r,int pos)
{
        //动态开点+复制整棵树 
	now=++tot;
	tree[now].l=tree[pre].l;
	tree[now].r=tree[pre].r;
	tree[now].siz=tree[pre].siz+1;
        //如果搜到底了,直接修改+返回 
	if(l==r)return ;
        //线段树二分 
	if(pos<=mid)update(tree[now].l,tree[pre].l,l,mid,pos);
	else update(tree[now].r,tree[pre].r,mid+1,r,pos);
    return ;
}
 
//3.查询区间第k小

int query(int pre,int now,int l,int r,int k)
{
	if(l==r)return l;
	int t=tree[tree[now].l].siz-tree[tree[pre].l].siz;//求出左儿子中存储的数值个数t 
	if(t>=k)return query(tree[pre].l,tree[now].l,l,mid,k);//若k<t,则说明k在左儿子中,向左儿子搜索 
	else return query(tree[pre].r,tree[now].r,mid+1,r,k-t);//反之,则说明k在右儿子中,向右儿子搜索。
	//注意要写k-t,因为左子树那边已经有了t个,只要在右儿子这里搜到k-t个即可。 
}

//4.查询通用模板

int query(int now,int l,int r,int x)//查询 
{
	if(l==r){return tree[now].v;}
	if(x<=mid)return query(tree[now].l,l,mid,x);
	else return query(tree[now].r,mid+1,r,x); 
}

一些数论相关模板

  • 埃氏筛
memset(vis,0,sizeof(vis));
for(int i=2;i<=n;i++)
{
  if(vis[i])continue;//若被标记,则说明不是质数。
  cout<<i<<endl;//输出质数
  for(int j=i;j*i<=n;j++)vis[i*j]++;//将被数标记为合数。
}
  • 试除法分解质因数
for(int i=2;i*i<=n;i++)
{
      if(n%i==0)
      {
           p[++cnt]=i;c[cnt]=0;
           while(n%i==0)n/=i,c[cnt]++;
      }
}
 
if(n>1) p[++cnt]=n,c[cnt]=1;//注意一下这里,如果这时候 n 还没被除到1,则说明 n 是质数。

  • 试除法求约数及约数个数
int f[maxn],cnt=0;//f数组用来存储约数,cnt表示约数个数。
for(int i=1;i*i<=n;i++)
{
    if(!(n%i)){f[++cnt]=i;if(i!=n/i)f[++cnt]=n/i;}//如果不是根号n,那就把n/i也存储进去。
}
for(int i=1;i<=m;i++)cout<<f[i]<<endl;
  • 倍数法求多个数的因数
vector<int>f[maxn];//由于直接开数组空间不够,所以开了动态数组vector
for(int i=1;i<=n;i++)
    for(int j=1;j<=n/i;j++)f[i*j].push_back(i);
for(int i=1;i<=n;i++)
    for(int j:f[i])cout<<f[i][j]<<'\n';

  • 欧几里得辗转相除法
int gcd(int a,int b){if(b==0)return a;return gcd(b,a%b);}

  • 试除法求单个数的欧拉函数
int phi(int n)
{
    int ans=n;
    for(int i=2;i<=n;i==)
    {
        if(n%i==0)
        {
            ans=ans/i*(i-1);//欧拉函数计算公式
            while(n%i==0)n/=i;//分解质因数
        }
    }
    if(n>1)ans=ans/n*(n-1);//参考分解质因数“代码实现”(n不是质数)
    return ans;
  • 线性筛求多个数欧拉函数

OIWiki:

int euler_phi(int x)
{
	memset(ip,1,sizeof(ip));
	int cnt=0;
	ip[1]=0;phi[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(ip[i]){p[++cnt]=i;phi[i]=i-1;}
		for(int j=1;j<=cnt&&i*p[j]<=n;j++)
		{
			ip[i*p[j]]=0;
			if(i%p[j])phi[i*p[j]]=phi[i]*phi[p[j]];
			else {phi[i*p[j]]=phi[i]*p[j];}
		}
	}
	return cnt;
}

算法竞赛进阶指南:

void euler(int n)
{
   memset(vis,0,sizeof(vis));
   m=0;
   for(int i=2;i<=n;i++)
   {
       if(vis[i]==0){v[i]=i,p[++cnt]=i;phi[i]=i-1;}
       //给当前的数 i 乘上一个质因子
       for(int j=1;j<=m;j++)
       {
           //i 有比p[j]更小的质因子,或者超出 n 的范围,停止循环。
           if(p[j]>v[i]||p[j]>n/i)break;
           //p[j] 是合数 i*p[j] 的最小质因子。
           vis[i*p[j]]=p[j];
           phi[i*p[j]]=phi[i]*(i%p[j]?p[j]-1:p[j]);
       }
   }
}
  • 扩展欧几里得算法(exgcd)
int exgcd(int a,int b,int &x,int &y)
{
    if(b==0){x=1;y=0;return a;}
    int d=exgcd(b,a%b,x,y);
    int t=x;x=y;y=t-y*(a/b);
    return d;
}
  • 乘法逆元

exgcd:

void exgcd(int a,int b,int &x,int &y)//注意这里是void而不是int,因为我们最后要求的不是exgcd,而是里面的 x。
{
  if(b==0){x=1;y=0;return ;}
  exgcd(b,a%b,y,x);//由于要求的直接是 x,就可以在这里直接把x和y swap一下即可。
  y-=x*(a/b);
}

费马小定理(快速幂):

int qpow(int a, int b) {
  int ans = 1;
  a=(a%p+p)%p;
  for (;b;b >>= 1) {
    if (b&1)ans=(a*ans)%p;
    a=(a*a)%p;
  }
  return ans;
}

线性求逆元

inv[0]=1;//注意这里一定要加上0的逆元~~我为此有一道题折腾了一个小时……
inv[1]=1;
for(int i=2;i<=n;i++)inv[i]=(p-p/i)*inv[p%i]%p;
  • 中国剩余定理(CRT)
int CRT(int k,int *a,int *b)//*a和*b表示上述n数组和a数组
{
    int n=1;ans=0;
    for(int i=1;i<=k;i++)n*=b[i];
    for(int i=1;i<=k;i++)
    {
        int m=n/b[i];
        int x=0,y=0;
        exgcd(m,b[i],x,y);//求逆元
        ans=(ans+a[i]*m*x%n)%n;
    }
    return (ans%n+n)%n;
}
  • 扩展中国剩余定理(EXCRT)
int EXCRT()
{
    int ans=b[1],M=a[1];
    for(int i=2;i<=n;i++)
    {
        int x=0,y=0;
        int A=M,B=a[i],C=(b[i]-ans%B+B)%B;
        int d=exgcd(A,B,x,y);
        x=mul(x,(C/d),(B/d));
        ans+=x*M;
        if(c%d)return -1;
        M*=B/d;ans=(ans+M)%M;
    }
    return ans;
}

矩阵相关

  1. 定义
struct mat{
	int n;
	int v[maxn][maxn];
	mat(){memset(v,0,sizeof(v));}
	mat(int N,int one=0)
	{
		n=N;memset(v,0,sizeof(v));
		if(one)for(int i=1;i<=n;i++)v[1][i]=1;
	}
};
  1. 矩阵乘法
mat operator*(mat A,mat B)
{
	mat C=mat(A.n);
	for(int i=1;i<=C.n;i++)
	for(int j=1;j<=C.n;j++)
	for(int k=1;k<=C.n;k++)
	  (C.v[i][j]+=A.v[i][k]*B.v[k][j])%=mod;
	return C;
}
  1. 矩阵快速幂
mat pw(mat A,int T)
{
	//cout<<A.n<<endl;
	mat ret=mat(A.n,1);
	while(T)
	{
		if(T&1)ret=ret*A;
		A=A*A;T>>=1;
	}
	return ret;
}

高斯消元法:

void gauss()
{
    for(int i=1;i<=n;i++)
    {
        bool f=false;
        for(int j=i;j<=n;j++)
        {
            if(a[j][i])
            {
                f=true;
                for(int k=1;k<=m;k++)swap(a[j][k],a[i][k]);
                double tt=a[i][i];
                for(int k=1;k<=m;k++)a[i][k]/=tt;
                break;
            }
        }
        if(!f){cout<<"No Solution"<<'\n';exit(0);}
        for(int j=1;j<=n;j++)
        {
            if(j==i)continue;
            double tt=a[j][i];
            for(int k=1;k<=m;k++)a[j][k]-=tt*a[i][k];
        }
    }
    for(int i=1;i<=n;i++)cout<<fixed<<setprecision(2)<<a[i][m]<<endl;
}

组合数学

拉格朗日插值

for(int i=1;i<=n;i++)
{
  	int s1=y[i]%mod,s2=1;
  	for(int j=1;j<=n;j++)
  	{
  		if(i!=j)(s1*=(k-x[j]))%=mod,(s2*=(x[i]-x[j]))%=mod; 
  	}
  	ans=(((ans+s1*inv(s2)+mod)%mod)+mod)%mod;
 }

自然数幂之和

int Lagrange()
{
	int ans=0,now=0;
	for(int i=1;i<=k+2;i++)
	{
		(now+=qpow(i,k))%=mod;
		int up=pr[i-1]*sf[i+1]%mod;
		int down=((k+2-i)&1?-1:1)*jc[i-1]%mod*jc[k+2-i]%mod;
		(ans+=now*up%mod*inv(down)%mod)%=mod;
    }
    return (ans+mod)%mod;
}

To be continued……

posted @ 2022-04-10 15:02  向日葵Reta  阅读(39)  评论(0编辑  收藏  举报