题记:noip复赛快要开始了,想想整理模板大概会有点用吧。
作为比赛的基础的话肯定是读入输出,打开文件吧。
#include<cstdio>
int main(){
freopen("*.in","r",stdin);
freopen("*.out","w",stdout);
return 0;
}
c++固定格式,不过用DEV会比较奇怪,即使不加上cstdio好像也不会出错。
这里正常的读入输出都不在重复,补充输入输出挂:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
void Rd(int &x){
char c;
x=0;
int f=1;
while(c=getchar(),!isdigit(c)&&c!='-');
if(c=='-')c=getchar(),f=-1;
do x=(x<<3)+(x<<1)+(c^'0');
while(c=getchar(),isdigit(c));
x=x*f;
}
其中那条isdigit用于判断该字符是否为函数,但我不太清楚在哪个头文件里。负数需特判.
void Print(int x){
if(x==0)return;
Print(x/10);
putchar((x%10)^'0');
}
void Pf(int x){
if(x<0)putchar('-'),x=-x;
else if(x==0){putchar('0');return;}
Print(x);
}
这里输出挂同样支持负数。
struct Tree{
int A[M];
struct node{
int l,r,v,las;
}tree[M*4];
void up(int p){
}
void down(int p){
}
#define Ls(x) (x<<1)
#define Rs(x) (x<<1|1)
void build(int l,int r,int p){//建树
tree[p].l=l,tree[p].r=r;
if(l==r){
// tree[p].v=A[l];
return;
}
int mid=l+r>>1;
build(l,mid,Ls(p));
build(mid+1,r,Rs(p));
up(p);
}
void update(int l,int r,int a,int p){//更新
if(tree[p].l==l&&tree[p].r==r){
// tree[p].v=(a);
return;
}
down(p);
int mid=tree[p].l+tree[p].r>>1;
if(mid<l)update(l,r,a,Rs(p));
else if(mid>=r)update(l,r,a,Ls(p));
else update(l,mid,a,Ls(p)),update(mid+1,r,a,Rs(p));
up(p);
}
int query(int l,int r,int p){//查询
// if(tree[p].l==l&&tree[p].r==r)return tree[p].v;
int mid=tree[p].l+tree[p].r>>1;
down(p);
if(mid<l)return query(l,r,Rs(p));
else if(mid>=r)return query(l,r,Ls(p));
else return (query(l,mid,Ls(p)),query(mid+1,r,Rs(p)));
}
}Tr;
这里线段树是可以延迟更新的,down和up不具体写明,但是四倍内存一定要开到,否则很容易爆数组,复杂度 O(nlog2n) 。
struct Bit{
int num[M],n;
void Add(int x,int a){//更新
while(x<=n){
// num[x]=(a);
x+=(x&-x);
}
}
int Ask(int x){//查询
int res=0;
while(x){
// res=(num[x]);
x-=(x&-x);
}
return res;
}
}Tr;
树状数组同样不具体写明应用,树状数组只需要开一倍内存,比较于线段树常数较小,而且简单精巧,复杂度同样 O(nlog2n) 。
int Fa[M];
int Find(int x){
if(Fa[x]==x)return x;
return Fa[x]=Find(Fa[x]);
}
void Init(){//预处理
for(int i=1;i<=n;i++)Fa[i]=i;
}
带权并查集这里不表,复杂度较为玄学,目测可以当 O(n) 来算(因为常数非常小),不过最好还是当成 O(nlog2n) 。
int Lg2[M];
struct ST{
int num[S][M],n,A[M];
void Init(){ //预处理
Lg2[0]=-1;
for(int i=1;i<=n;i++)Lg2[i]=Lg2[i>>1]+1;
for(int i=1;i<=n;i++)num[0][i]=i;
for(int i=1;i<S;i++){
for(int j=1;j<=n;j++){
int k=j+(1<<(i-1));
if(k<=n){
int a=num[i-1][j],b=num[i-1][k];
if(A[a]>A[b])num[i][j]=b;
else num[i][j]=a;
}else num[i][j]=num[i-1][j];
}
}
}
int query(int l,int r){//查询
int k=Lg2[r-l+1];
int a=num[k][l],b=num[k][r-(1<<k)+1];
return A[a]>A[b]?b:a;
}
}St;
这里的ST表为求最小值的版本,其中的S为 log2M ,复杂度为 O(nlog2n) ,查询 O(1) 。
struct Big{
int num[M],len;
Big(){memset(num,0,sizeof(num)),len=1;};//清零
bool operator <(const Big &a)const{// 判断大小
if(len<a.len)return true;
if(len>a.len)return false;
for(int i=len;i>=1;i--){
if(num[i]<a.num[i])return true;
if(num[i]>a.num[i])return false;
}
return false;
}
Big operator +(const Big &a)const{//高精加
Big res;
res.len=max(len,a.len);
for(int i=1;i<=res.len;i++){
res.num[i]+=num[i]+a.num[i];
res.num[i+1]+=res.num[i]/P;
res.num[i]%=P;
}
if(res.num[res.len+1])res.len++;
return res;
}
Big operator -(const Big &a)const{// 高精减 (大数减小数)
Big res;
res.len=len;
for(int i=1;i<=len;i++){
res.num[i]+=num[i]-a.num[i];
if(res.num[i]<0)res.num[i]+=P,res.num[i+1]--;
}
while(!res.num[res.len])res.len--;
return res;
}
Big operator *(const Big &a)const{// 高精乘
Big res;
for(int i=1;i<=len;i++){
for(int j=1;j<=a.len;j++){
res.num[i+j-1]+=(num[i]*a.num[j]);
res.num[i+j]+=res.num[i+j-1]/P;
res.num[i+j-1]%=P;
}
}
res.len=len+a.len;
if(res.num[res.len+1])res.len++;
return res;
}
Big operator /(const int &a)const{// 高精除低精
Big res;
for(int i=1;i<=len;i++)res.num[i]=num[i];
res.len=len;
for(int i=res.len;i>=1;i--){
res.num[i-1]+=P*(res.num[i]%a);
res.num[i]/=a;
}
while(!res.num[res.len])res.len--;
return res;
}
void str_to_int(string a){// 字符串变成Bigint
len=a.length();
for(int i=0;i<len;i++)num[len-i]=a[i]^'0';
}
void Add(){// 加1
num[1]++;
for(int i=1;i<=len;i++)
num[i+1]+=num[i]/P,num[i]%=P;
if(num[len+1])len++;
}
void Dec(){// 减1
num[1]--;
for(int i=1;i<=len;i++)
if(num[i]<0)num[i]+=P,num[i+1]--;
if(!num[len])len--;
}
void Print(){// 输出
for(int i=len;i>=1;i--)printf("%d",num[i]);
puts("");
}
Big operator /(const Big &a)const{// 高精除
Big L=(/*下界*/),R=(/*上界*/),ans;
while(!(R<L)){
Big mid=(L+R)/2;
Big tmp=mid*y;
if(x<tmp)R=mid,R.Dec();
else L=mid,L.Add(),ans=mid;;
}
}
};
这里既支持高精除高精,也支持高精除低精,其中的P为选择的进制,这里设为10,P的不同会导致str_to_int和Print两条函数设置的不同。
struct node{
int to,nxt;
}eg[M];
int tot,head[M];
void Add(int x,int y){
tot++;
eg[tot].to=y;
eg[tot].nxt=head[x];
head[x]=tot;
}
使用正向表的时候一定要计算好结构体数组的内存,否则炸了都不知道怎么回事。不过据说正向表会比vector快(STL因为全面性)。
struct node{
int to,v;
bool operator <(const node &a)const{
return v>a.v;
}
};
vector<node>v[M];
priority_queue<node>Q;
int dis[M];
bool mark[M];
int dijkstra(int s,int t){
while(!Q.empty())Q.pop();
memset(mark,0,sizeof(mark));
memset(dis,63,sizeof(dis));
dis[s]=0;
Q.push((node){s,0});
while(!Q.empty()){
node now=Q.top();
Q.pop();
int x=now.to;
if(mark[x])continue;
if(x==t)return now.v;
mark[x]=1;
for(int i=0;i<v[x].size();i++){
int y=v[x][i].to;
if(mark[y])continue;
if(dis[y]>dis[x]+v[x][i].v){
dis[y]=dis[x]+v[x][i].v;
Q.push((node){y,dis[y]});
}
}
}
return -1;
}
dijkstra是单源最短路算法,复杂度为 O(nlog2m) 。
struct node{
int u,v,cost;
}eg[M];
int m;//边数
int dis[M];
bool mark[M];
#define oo 1061109567
int Bellman_Ford(int s,int t){
memset(dis,63,sizeof(dis));
dis[s]=0;
while(true){
bool f=false;
for(int i=1;i<=m;i++){
node e=eg[i];
if(dis[e.v]>dis[e.u]+e.cost){
dis[e.v]=dis[e.u]+e.cost;
f=true;
}
}
if(f==false)break;
}
return dis[t]==oo?-1:dis[t];
}
复杂度目测上界是 O(m∗n) ,但是有时候跑的就是比dijkstra快很多。也是单源最短路,可以判负环。
struct node{
int to,v;
};
vector<node>v[M];
int Q[M],dis[M];
bool mark[M];
#define oo 1061109567
int SPFA(int s,int t){
int l=0,r=0;
memset(dis,63,sizeof(dis));
dis[s]=0;
Q[r++]=s;
mark[s]=1;
while(l<r){
int now=Q[l++];
mark[now]=0;
for(int i=0;i<v[now].size();i++){
int p=v[now][i].to;
if(dis[p]>dis[now]+v[now][i].v){
dis[p]=dis[now]+v[now][i].v;
if(!mark[p])Q[r++]=p,mark[p]=1;
}
}
}
return dis[t]==oo?-1:dis[t];
}
复杂度玄学,这里就不说了,也是单源的。
int eg[M][M];
int n,m,s,t;
#define oo 1061109567
int Floyd(int s,int to){
for(int t=1;t<=n;t++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
if(eg[i][j]>eg[i][t]+eg[t][j])eg[i][j]=eg[i][t]+eg[t][j];
}
}
return eg[s][to]==oo?-1:eg[s][to];
}
复杂度 O(n3) ,但是是多源最短路,可以判断负环的存在。
struct node{
int u,v,cost;
bool operator <(const node &a)const{
return cost<a.cost;
}
}eg[M];
int Fa[M],n,m;
int Find(int x){
if(Fa[x]==x)return x;
return Fa[x]=Find(Fa[x]);
}
void Init(){
sort(eg+1,eg+m+1);
for(int i=1;i<=n;i++)Fa[i]=i;
}
int kruskal(){
int res=0;
for(int i=1;i<=m;i++){
int fu=Find(eg[i].u);
int fv=Find(eg[i].v);
if(fu!=fv){
Fa[fu]=fv;
res+=eg[i].cost;
}
}
return res;
}
kruskal是应用比较多的最小生成树算法,复杂度 O(mlog2m)
struct node{
int to,v;
bool operator <(const node &a)const{
return v>a.v;
}
};
int n,m,dis[N],x,y,z,ans;
vector<node>e[M];
priority_queue<node>Q;
bool mark[N];
int dis[M];
int Prim(){
int ans=0;
memset(dis,63,sizeof(dis));
dis[1]=0;
Q.push((node){1,0});
while(!Q.empty()){
node now=Q.top();
Q.pop();
int k=now.to;
if(mark[k])continue;
ans+=now.v;
mark[k]=1;
for(int i=0;i<e[k].size();i++){
int t=e[k][i].to;
if(mark[t])continue;
if(dis[t]>e[k][i].v){
dis[t]=e[k][i].v;
Q.push((node){t,dis[t]});
}
}
}
return ans;
}
与dijkstra算法有较大的相似处,只不过更正了小部分。复杂度与dijkstra也是一样的。
int Fa[S][N],dep[N];
vector<int>v[N];
void DFS(int now,int p,int d){
Fa[0][now]=p;
dep[now]=d;
for(int i=0;i<v[now].size();i++){
int t=v[now][i];
if(t==p)continue;
DFS(t,now,d+1);
}
}
int n,m;
void Init(){
for(int i=1;i<S;i++){
for(int j=1;j<=n;j++){
if(Fa[i-1][j]<0)Fa[i][j]=Fa[i-1][j];
else Fa[i][j]=Fa[i-1][Fa[i-1][j]];
}
}
}
int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
int step=dep[x]-dep[y];
for(int i=S-1;i>=0;i--)
if((1<<i)&step)x=Fa[i][x];
if(x==y)return x;
for(int i=S-1;i>=0;i--)
if(Fa[i][x]!=Fa[i][y])x=Fa[i][x],y=Fa[i][y];
return Fa[0][x];
}
复杂度为 O(nlog2n) 。
int Fa[N],dep[N],sz[N],Sn[N];
vector<int>v[N];
void P_DFS(int now,int p,int d){
Fa[now]=p;
dep[now]=d;
sz[now]=1;
for(int i=0;i<v[now].size();i++){
int t=v[now][i];
if(t==p)continue;
P_DFS(t,now,d+1);
sz[now]+=sz[t];
if(sz[Sn[now]]<sz[t])Sn[now]=t;
}
}
int top[N],szn;
void M_DFS(int now,int p,int tp){
top[now]=tp;
if(Sn[now])M_DFS(Sn[now],now,tp);
for(int i=0;i<v[now].size();i++){
int t=v[now][i];
if(t==p||t==Sn[now])continue;
M_DFS(t,now,t);
}
}
void Init(){
P_DFS(1,-1,0);
M_DFS(1,-1,1);
}
int LCA(int x,int y){
while(top[x]!=top[y]){
int tx=top[x],ty=top[y];
if(dep[tx]>dep[ty])x=Fa[tx];
else y=Fa[ty];
}
return dep[x]>dep[y]?y:x;
}
树链剖分的复杂度应该也是 O(nlog2n) ,但不知道为什么就是比倍增要快一点。
bool Prime(int x){
for(int i=2;i*i<=x;i++)
if(x%i==0)return false;
return true;
}
用于判断单个素数的话往往这种办法会比较好。
void Prime(int x){
for(int i=2;i<M;i++){
if(!mark[i]){
for(int j=i+i;j<M;j+=i)
mark[j]=1;
}
}
}
其中所有没被标记的点都是素数。这种办法可以筛出2到n的所有素数,不过复杂度比较玄学,据说是 O(nlog2log2n) 。两种筛法各有不同,在不同的情况灵活运用好了。
int Gcd(int a,int b,int &x,int &y){
if(b==0){x=1;y=0;return a;}
int res=Gcd(b,a%b,y,x);
y-=a/b*x;
return res;
}
同余方程一直是一个很奇怪的东西,反正我是没怎么记熟的。大概记住就好了。
int phi(int x){
int res=x;
for(int i=2;i*i<=x;i++){
if(x%i==0){
res=res/i*(i-1);
while(x%i==0)x/=i;
}
}
if(x>1)res=res/x*(x-1);
return res;
}
复杂度和筛单个素数是一样的。
int ph[M];
void Init(){
for(int i=1;i<M;i++)ph[i]=i;
for(int i=2;i<M;i++){
if(ph[i]==i){//这是个素数
ph[i]=i-1;
for(int j=i+i;j<M;j+=i)
ph[j]=ph[j]/i*(i-1);
}
}
}
这跟区间筛素数也是差不多的,复杂度也是那个比较魔性的函数。当然用法也是差不多的,看着耍吧。
int Fast(int x,int y,int P){//(x^y)%P
int res=1;
while(y){
if(y&1)res=1ll*res*x%P;
y>>=1;
x=1ll*x*x%P;
}
return res;
}
快速幂在许多方面都会很有用。
int Gcd(int a,int b,int &x,int &y){//res为gcd(a,b).a,b,x,y满足a*x+b*y==1 (x为a%b的逆元)
if(b==0){x=1,y=0;return a;}
int res=Gcd(b,a%b,y,x);
y-=a/b*x;
return res;
}
拓展欧几里得也是比较有用的,在数论一方面是非常恶心的东西。
就这样吧,也不知道这里的模板会有多少能用到的。
以上