Templates by Hanggoash

整数二分

边界条件处理不好非常容易写挂
以简单的二分查找为例,有以下两种写法

点击查看代码
int bsearch(int x,int l,int r)
{
	if(l>=r)return l;
	int mid=l+r>>1;
	if(a[mid]<x)return bsearch(x,mid+1,r);
	else return bsearch(x,l,mid);
}
点击查看代码
int bsearch(int x,int l,int r)
{
	if(l>=r)return l;
	int mid=(l+r+1)>>1;
	if(a[mid]<=x)return bsearch(x,mid,r);
	else return bsearch(x,l,mid-1);
}

简单来说,首先需要知道计算 \(mid\) 的时候是下取整的。
判断条件中取等号的那个需要保留 \(mid\) ,因此这时 \(mid=(l+r+1)>>1\) ,防止当查询右子区间长度为 \(1\) 的时候造成死循环,例如 \([2,3]\)

如果需要取等的一边在左子区间,那就不需要 ,因为会被下取整,不取等号不需要保留,所以直接 \(+-1\)

快速幂

点击查看代码
inline int power(int a,int b,int p)
{
	long long ans=1;
	while(b)
	{
		if(b&1)ans=ans*a%p;
		a=a*a%p;
		b>>=1;
	}
	return ans;
}

线段树

1

点击查看代码
#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void re(T &x)
{
	x=0;
	int f=1;char c=getchar();
	while(!isdigit(c))
	{
		if(c=='-')f=-1;
		c=getchar();
	}
	while(isdigit(c))
	{
		x=(x<<1)+(x<<3)+(c^48);
		c=getchar();
	}
	x*=f;
}
template<typename T>inline void wr(T x)
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)wr(x/10);
	putchar(x%10^48);
}
const int N=1e6+1;
struct SGT
{
	int l,r;
	long long val,tag;
}tr[N<<2];
int a[N],n,m,x,y,k,opt;
long long calc(int x)
{
	return tr[x].val+(tr[x].r-tr[x].l+1)*tr[x].tag;
}
void pd(int x)
{
	tr[x<<1].tag+=tr[x].tag,
	tr[x<<1|1].tag+=tr[x].tag;
	tr[x].tag=0;
}
void pu(int x)
{
	tr[x].val=calc(x<<1)+calc(x<<1|1);
}
inline void build(int x,int l,int r)
{
	tr[x].l=l,tr[x].r=r;
	if(l==r)
	{
		tr[x].val=a[l];return;
	}
	int mid=l+r>>1;
	build(x<<1,l,mid),build(x<<1|1,mid+1,r);
	pu(x);
}
inline void modify(int x,int l,int r,int v){
	if(l<=tr[x].l&&tr[x].r<=r)
	{
		tr[x].tag+=v;
		return;
	}
	pd(x);
	int mid=tr[x].l+tr[x].r>>1;
	if(l<=mid)modify(x<<1,l,r,v);
	if(r>mid)modify(x<<1|1,l,r,v);
	pu(x);
}
inline long long query(int x,int l,int r)
{
	if(l<=tr[x].l&&tr[x].r<=r)return calc(x);
	pd(x);
	int mid=tr[x].l+tr[x].r>>1;
	long long ans=0;
	if(l<=mid)ans+=query(x<<1,l,r);
	if(r>mid)ans+=query(x<<1|1,l,r);
	pu(x);
	return ans;
}
void pre()
{
	re(n),re(m);
	for(int i=1;i<=n;i++)re(a[i]);
	build(1,1,n);
}
int main()
{
	pre();
	while(m--)
	{
		re(opt);
		if(opt==1)
		{
			re(x),re(y),re(k);
			modify(1,x,y,k);
		}
		else
		{
			re(x),re(y);
			wr(query(1,x,y)),putchar('\n');
		}
	}
	return 0;
}

矩阵快速幂

点击查看代码
struct Matrix
{
	int n,m;
	int a[maxn][maxn];
	Matrix(){memset(a,0,sizeof a);}
};
Matrix operator *(Matrix a,Matrix b)
{
	Matrix tmp;
	tmp.n=a.n,tmp.m=b.m;
	for(int i=1;i<=a.n;++i)
	{
		for(int j=1;j<=b.m;++j)
		{
			for(int k=1;k<=a.m;++k)
			{
				tmp.a[i][j]=(tmp.a[i][j]+a.a[i][k]*b.a[k][j])%MOD;
			}
		}
	}
	return tmp;
}
Matrix base(int x)
{
	Matrix tmp;
	tmp.n=tmp.m=x;
	for(int i=1;i<=x;++i)tmp.a[i][i]=1;
	return tmp;
}
Matrix power(Matrix a,int b)
{
	Matrix ans=base(a.n);
	while(b)
	{
		if(b&1)ans=ans*a;
		a=a*a;
		b>>=1;
	}
	return ans;
}

LCA

倍增

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+2;
int F[N][22],dep[N];
int n,m,s;
vector<int> E[N];
inline void dfs(int x)
{
	for(int i=0;i<E[x].size();i++)
	{
		int v=E[x][i];
		if(v==F[x][0])continue;
		F[v][0]=x,dep[v]=dep[x]+1;
		dfs(v); 
	}
}
void pre()
{
	cin>>n>>m>>s;
	int u,v;
	for(int i=1;i<=n-1;i++)
	{
		cin>>u>>v;
		E[u].push_back(v),E[v].push_back(u);
	}
	dep[s]=1,dfs(s);
	for(int j=1;j<=20;j++)
		for(int i=1;i<=n;i++)
			F[i][j]=F[F[i][j-1]][j-1]; 
}
inline int lca(int x,int y)
{
	if(dep[x]<dep[y])swap(x,y);
	for(int i=20;i>=0;i--)
		if(dep[F[x][i]]>=dep[y])x=F[x][i];
	if(x==y)return x;
	for(int i=20;i>=0;i--)
		if(F[x][i]!=F[y][i])x=F[x][i],y=F[y][i];
	return F[x][0];
}
int main()
{
	pre();
	int u,v;
	while(m--)
	{
		cin>>u>>v;
		cout<<lca(u,v)<<endl;
	}
	return 0;
}

树链剖分

最短路

Dijkstra

点击查看代码
inline void dijkstra(int s)
{
	priority_queue< pair<int,int> > q;
	memset(dis,0x3f,sizeof dis);
	memset(vis,0,sizeof vis);
	dis[s]=0,q.push(make_pair(0,s));
	while(q.size())
	{
		int x=q.top().second;q.pop();
		if(vis[x])continue;
		vis[x]=1;
		for(int i=head[x];i;i=E[i].nex)
		{
			int v=E[i].v;
			if(dis[v]>dis[x]+E[i].w)
			{
				dis[v]=dis[x]+E[i].w;
				if(!vis[v])q.push(make_pair(-dis[v],v));
			}
		}
	}
}

可以用堆进行优化,默认大根堆,直接push(-dis[v],v)就好了
DJ的本质就是贪心,\(vis\) 数组代表有没有被松弛过,每个点只会被松弛一次就能算出正确值。

SPFA

点击查看代码
inline void spfa(int x)
{
	queue<int>q;
	memset(dis,0x7f,sizeof dis);
	memset(vis,0,sizeof vis);
	
	q.push(x);vis[x]=1;dis[x]=0;
	while(!q.empty())
	{
		int x=q.front();vis[x]=0,q.pop();
		for(int i=head[x];i;i=E[i].nex)
		{
			int v=E[i].v;
			if(dis[x]+E[i].w<dis[v])
			{
				dis[v]=dis[x]+E[i].w;
				if(!vis[x])q.push(v),vis[v]=1;
			}
		}

	}
}

注意spfa的vis数组代表是否在队列中,可以经过多次修改;而dij的vis数组代表是否松弛过,只会被修改一次

posted @ 2024-08-20 09:40  Hanggoash  阅读(5)  评论(0编辑  收藏  举报
动态线条
动态线条end