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数组代表是否松弛过,只会被修改一次
为什么要练,为什么要写?
引用一句让我幡然悔悟的话:
“练了不一定写的出来正解,不练一定写不出来正解”
本文来自博客园,作者:Hanggoash,转载请注明原文链接:https://www.cnblogs.com/Hanggoash/p/18368783