模板(未完结)
模板
存放一些学过的模板,未完结。
- 离散化
//方法一: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;
}
矩阵相关
- 定义
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;
}
};
- 矩阵乘法
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;
}
- 矩阵快速幂
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……