算法

蒟蒻的模板

开一个长期更新(咕咕咕)的坑,主要是一些较为基础的模板

题目编号以洛谷为准,目前只放代码,至于原理啥的以后慢慢来吧。

P3379 【模板】最近公共祖先(LCA)注意t的取值

#include<bits/stdc++.h>
using namespace std;
const int size=500010;
int n,m,t,f[size][25],root;
int head[size],ver[2*size],Next[2*size],d[size],tot;
queue<int>q;
void add(int x,int y){
	ver[++tot]=y;Next[tot]=head[x];head[x]=tot;
}
void bfs(){
	q.push(root);d[root]=1;
	while(q.size()){
		int x=q.front();q.pop();
		for(int i=head[x];i;i=Next[i]){
			int y=ver[i];
			if(d[y]) continue;
			d[y]=d[x]+1;
			f[y][0]=x;
			for(int j=1;j<=t;++j){
				f[y][j]=f[f[y][j-1]][j-1];
			}
			q.push(y);
		}
	}
}
int lca(int x,int y){
	if(d[x]>d[y]) swap(x,y);
	for(int i=t;i>=0;i--){
		if(d[f[y][i]]>=d[x]){
			y=f[y][i];
		} 
	}
	if(x==y) return x;
	for(int i=t;i>=0;i--){
		if(f[y][i]!=f[x][i]){
			y=f[y][i];
			x=f[x][i];
		}
	}
	return f[x][0];
}
int main(){
	scanf("%d %d %d",&n,&m,&root);
	t=((int)(log(n)/(log(2))))+1;
	for(int i=1;i<n;++i){
		int x,y;
		scanf("%d %d",&x,&y);
		add(x,y);add(y,x);
	}
	bfs();
	for(int i=1;i<=m;++i){
		int x,y;
		scanf("%d %d",&x,&y);
		printf("%d\n",lca(x,y));
	}
	return 0;
}

P3378 【模板】堆(注意更新当前坐标

#include<bits/stdc++.h>
using namespace std;
const int size=1000010;
int a[size],n,k,x,add;
void shiftdown(int x){
	int t;bool flag=1;
	while(2*x<=add&&flag){
		if(a[x*2]<a[x]) t=x*2;
		else t=x;
		if(2*x+1<=add&&a[x*2+1]<a[t]) t=2*x+1;
		if(t!=x){
			swap(a[t],a[x]);
			x=t;
		} else flag=0;
	}
}
void updown(int x){
	bool flag=1;
	if(x==1) return;
	while(x!=1&&flag){
		if(a[x/2]>a[x]) swap(a[x/2],a[x]);
		else flag=0;
		x/=2;
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",&k);
		if(k==1){
			scanf("%d",&x);
			a[++add]=x;
			updown(add);
		}
		else if(k==2) printf("%d\n",a[1]);
		else{
			a[1]=a[add--];
			shiftdown(1);
		}
	}
	return 0;
}

P4549 【模板】裴蜀定理( gcd 很重要

#include<bits/stdc++.h>
using namespace std;
int n,x,ans;
int gcd(int a,int b){
	return b?gcd(b,a%b):a;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",&x);
		x=x<0?-x:x;
		ans=gcd(x,ans);
	}printf("%d",ans);
	return 0;
}

P3390 【模板】矩阵快速幂(不开 long long 见祖宗 n,k 都要开。

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
long long n,k;
struct node{
	long long s[110][110];
}a,ans;
node mul(node a,node b){
	node c;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			c.s[i][j]=0;
		}
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			for(int k=1;k<=n;++k){
				c.s[i][j]=(c.s[i][j]+(a.s[i][k]*b.s[k][j])%mod)%mod;
			}
		}
	}
	return c;
}
int main(){
	scanf("%lld %lld",&n,&k);
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			scanf("%lld",&a.s[i][j]);
			ans.s[i][i]=1;
		}
	}
	while(k){
		if(k&1) ans=mul(ans,a);
		a=mul(a,a);
		k>>=1;
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			printf("%lld ",ans.s[i][j]%mod);
		}printf("\n");
	}
	return 0;
}

P3385 【模板】负环

注意初始化,输出,还有spfa出了队列标记要变

#include<bits/stdc++.h>
using namespace std;
const int N=2010;
const int M=6010;
int tot,deg[N],head[N],ver[M],edge[M],Next[M];
int v[N],dis[N];
queue<int>q;
int T,n,m;
void add(int x,int y,int z){
	ver[++tot]=y;edge[tot]=z;Next[tot]=head[x];head[x]=tot;
}
bool spfa(){
	memset(v,0,sizeof(v));
	memset(deg,0,sizeof(deg));
	memset(dis,0x3f,sizeof(dis));
	dis[1]=0;v[1]=1;q.push(1);
	while(q.size()){
		int x=q.front();q.pop();v[x]=0;
		if(deg[x]==n-1) return false;deg[x]++;
		for(int i=head[x];i;i=Next[i]){
			int y=ver[i];
			if(dis[y]>dis[x]+edge[i]){
				dis[y]=dis[x]+edge[i];
				if(!v[y]){v[y]=1,q.push(y);}
			}
		}
	}return true;
}
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d %d",&n,&m);
		memset(head,0,sizeof(head));tot=0;
		for(int i=1;i<=m;++i){
			int x,y,z;
			scanf("%d %d %d",&x,&y,&z);
			z>=0?add(x,y,z),add(y,x,z):add(x,y,z);
		}
		spfa()?printf("N0\n"):printf("YE5\n");
	}
	return 0;
}

P3372 【模板】线段树 1

#include<bits/stdc++.h>
using namespace std;
const int size=100010;
struct node{
	long long l,r,data,add;
}t[4*size];
long long a[size];
int n,m,num,x,y;
long long k;
void build(long long p,long long l,long long r){
	t[p].l=l,t[p].r=r;
	if(l==r){t[p].data=a[l];return;}
	long long mid=(l+r)>>1;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	t[p].data=t[p*2].data+t[p*2+1].data;
}
void lazy(long long p){
	if(t[p].add){
		t[p*2].data+=t[p].add*(t[p*2].r-t[p*2].l+1);
		t[p*2+1].data+=t[p].add*(t[p*2+1].r-t[p*2+1].l+1);
		t[p*2].add+=t[p].add;
		t[p*2+1].add+=t[p].add;
		t[p].add=0;
	}
} 
void change(long long p,long long l,long long r,long long v){
	if(l<=t[p].l&&r>=t[p].r){t[p].data+=v*(t[p].r-t[p].l+1);t[p].add+=v;return;}
	lazy(p);
	int mid=(t[p].l+t[p].r)>>1;
	if(l<=mid) change(p*2,l,r,v);
	if(r>mid) change(p*2+1,l,r,v);
	t[p].data=t[p*2].data+t[p*2+1].data;
}
long long ask(long long p,long long l,long long r){
	if(l<=t[p].l&&r>=t[p].r) return t[p].data;
	lazy(p);
	long long mid=(t[p].l+t[p].r)>>1;
	long long val=0;
	if(l<=mid) val+=ask(p*2,l,r);
	if(r>mid) val+=ask(p*2+1,l,r);
	return val;
}
int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
	build(1,1,n);
	for(int i=1;i<=m;++i){
		scanf("%d",&num);
		if(num==1){
			scanf("%d %d %lld",&x,&y,&k);
			change(1,x,y,k);
		}else{
			scanf("%d %d",&x,&y);
			printf("%lld\n",ask(1,x,y));
		}
	}
	return 0;
}

P4777 【模板】扩展中国剩余定理(EXCRT)

#include<bits/stdc++.h>
using namespace std;
const int maxn=300010;
long long a[maxn],b[maxn],ans;
int n;
long long clu(long long a,long long b,long long p){
	long long tmp=0;
	while(b>0){
		if(b&1) tmp=(tmp+a)%p;
		a=(a+a)%p;
		b>>=1;
	}
	return tmp;
}
long long exgcd(long long a,long long b,long long &x,long long &y){
	if(b==0){
		x=1,y=0;
		return a;
	}  
	long long gcd=exgcd(b,a%b,x,y);
	long long tmp=x;
	x=y,y=tmp-a/b*y;
	return gcd;
}
long long solve(){
	long long x,y,k;
	long long M=a[1];
	ans=b[1];
	for(int i=2;i<=n;++i){
		long long ai=M,bi=a[i],c=(b[i]-ans%bi+bi)%bi;
		long long gcd=exgcd(ai,bi,x,y),tmp=bi/gcd;
		if(c%gcd!=0) return -1;
		x=clu(x,c/gcd,tmp);
		ans+=x*M;
		M*=tmp;
		ans=(ans%M+M)%M;
	}
	return (ans%M+M)%M;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%lld %lld",&a[i],&b[i]);
	}
	printf("%lld",solve());
	return 0;
}

P3382 【模板】三分法

秦九昭公式求多项式一开始打反了。。。什么 lj 样例,次数大的先扫,注意从0开始,其余三分模板

#include<bits/stdc++.h>
using namespace std;
const double esp=1e-9;
int n;
double l,r,ans,xi[20];
double clu(double x){
	double ans=0;
	for(int i=0;i<=n;++i){
		ans=ans*x+xi[i];
	}
	return ans;
}
int main(){
	scanf("%d %lf %lf",&n,&l,&r);
	for(int i=0;i<=n;++i){
		scanf("%lf",&xi[i]);
	}
	while(l+esp<=r){
		double lm=l+(r-l)/3.0;
		double rm=r-(r-l)/3.0;
		if(clu(lm)<clu(rm)) l=lm;
		else r=rm;
	}printf("%.5lf",l);
	return 0;
}

P2197 【模板】nim游戏

水题不多说,可以参照hdu 1850

#include<bits/stdc++.h>
using namespace std;
int T,n;
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		int ans=0,k;
		for(int i=1;i<=n;++i) scanf("%d",&k),ans^=k;
		ans?printf("Yes\n"):printf("No\n"); 
	}
	return 0;
}

P4779 【模板】单源最短路径(标准版)

dijkstra 无法处理负环,并且写法上同spfa有差异。

注意注意!!spfa 中 v 表示是否在队列里,而 dijkstra 表示是否拓展

#include<bits/stdc++.h>
using namespace std;
const int size=100010;
int n,m,s;
int head[size],ver[2*size],Next[2*size],edge[2*size],tot;
int dis[size],v[size];
struct node{
	int dis;
	int num;
	bool operator <(const node &x) const{
		return x.dis<dis;
	} 
};
priority_queue<node>q;
void add(int x,int y,int z){
	ver[++tot]=y;edge[tot]=z;Next[tot]=head[x];head[x]=tot;
}
void dija(){
	memset(dis,0x3f,sizeof(dis));
	memset(v,0,sizeof(v));
	dis[s]=0;q.push((node){0,s});
	while(q.size()){
		int x=q.top().num;q.pop();
		if(v[x]) continue;v[x]=1;//只能扩展一次
		for(int i=head[x];i;i=Next[i]){
			int y=ver[i];
			if(dis[y]>dis[x]+edge[i]){
				dis[y]=dis[x]+edge[i];
				if(!v[y]){q.push((node){dis[y],y});}//这里不需要标记
			}
		}
	}
}
int main(){
	scanf("%d %d %d",&n,&m,&s);
	for(int i=1;i<=m;++i){
		int x,y,z;
		scanf("%d %d %d",&x,&y,&z);
		add(x,y,z);
	}
	dija();
	for(int i=1;i<=n;++i){
		printf("%d ",dis[i]);
	}
	return 0;
}

P3386 【模板】二分图匹配(匈牙利算法

连单向边,并且判断边的存在才能去匹配

#include<bits/stdc++.h>
using namespace std;
const int size=1010;
int n,m,e;
int a[size][size],v[size],match[size];
bool dfs(int x){
	for(int i=1;i<=m;++i){
		if(a[x][i]){
			if(v[i]) continue;
			v[i]=1;
			if(match[i]==-1||dfs(match[i])){
				match[i]=x;
				return 1;
			}
		}
	}return 0;
}
int solve(){
	int ans=0;
	memset(match,-1,sizeof(match));
	for(int i=1;i<=n;++i){
		memset(v,0,sizeof(v));
		if(dfs(i)) ans++;
	}
	return ans;
}
int main(){
	scanf("%d %d %d",&n,&m,&e);
	for(int i=1;i<=e;++i){
		int x,y;
		scanf("%d %d",&x,&y);
		if(x>=1&&x<=n&&y>=1&&y<=m) a[x][y]=1;
	}
	printf("%d",solve());
	return 0;
}

P3366 【模板】最小生成树 Kruskal

#include<bits/stdc++.h>
using namespace std;
const int N=5010;
const int M=400010;
struct node{
	int u,v,dis;
}edge[M];
int n,m,tot=1;
int fa[N];
long long ans;
bool cmp(node a,node b){
	return a.dis<b.dis;
}
int find(int x){
	return x==fa[x]?x:fa[x]=find(fa[x]);
}
int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;++i){
		int x,y,z;
		scanf("%d %d %d",&edge[i].u,&edge[i].v,&edge[i].dis);
	}
	for(int i=1;i<=n;++i) fa[i]=i;
	sort(edge+1,edge+1+m,cmp);
	for(int i=1;i<n;++i){
		while(find(edge[tot].u)==find(edge[tot].v)) tot++;
		fa[find(edge[tot].u)]=find(edge[tot].v);ans+=edge[tot].dis;
	}printf("%lld",ans);
	return 0;
}

P3366 【模板】最小生成树 Prim

#include<bits/stdc++.h>
using namespace std;
const int N=5010;
const int M=400010;
int n,m;
long long ans;
int tot,head[N],ver[M],Next[M],edge[M];
int v[N],dis[N];
void add(int x,int y,int z){
	ver[++tot]=y;edge[tot]=z;Next[tot]=head[x];head[x]=tot;
}
int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;++i){
		int x,y,z;
		scanf("%d %d %d",&x,&y,&z);
		add(x,y,z);add(y,x,z);
	}
	memset(dis,0x3f,sizeof(dis));
	memset(v,0,sizeof(v));
	v[1]=1;dis[1]=0;
	for(int i=head[1];i;i=Next[i]){
		int y=ver[i],z=edge[i];
		dis[y]=min(dis[y],z);
	}
	for(int t=1;t<n;++t){
		int minn=1<<30;
		int op=1;
		for(int i=1;i<=n;++i){
			if(!v[i]&&dis[i]<minn){
				minn=dis[i];
				op=i;
			}
		}
		if(v[op]) continue;
		v[op]=1;ans+=minn;
		for(int i=head[op];i;i=Next[i]){
			int y=ver[i];
			if(!v[y]&&dis[y]>edge[i]){
				dis[y]=edge[i];
			}
		}
	}
	ans>1<<30?printf("orz"):printf("%lld",ans);
	return 0;
}

发表评论