CSP前把重要的板子全部打一遍吧,顺便放一些下饭集锦(在每个板子的下面)
图论
SPFA
ll dis[N];
bool vis[N];
queue<int> q;
void spfa(int s)
{
memset(vis,0,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
q.push(s);vis[s]=1;dis[s]=0;
while(!q.empty())
{
int u=q.front();q.pop();vis[u]=0;
for(register int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(dis[v]>dis[u]+edge[i].dis)
{
dis[v]=dis[u]+edge[i].dis;
if(!vis[v])
{
q.push(v);
vis[v]=1;
}
}
}
}
}
注意事项:
没什么好注意的,spfa极其优美,手感舒适;
下饭集锦:
脑子一抽把v搞成了edge[i].next
Dijkstra
struct Node
{
int u;
ll dis;
bool operator < (const Node &a) const
{
return a.dis<dis;
}
};
ll dis[N];
bool vis[N];
priority_queue<Node> q;
void dijkstra(int s)
{
memset(vis,0,sizeof(vis));
memset(dis,0x7f,sizeof(dis));
q.push(Node{s,0});dis[s]=0;
while(!q.empty())
{
int u=q.top().u;q.pop();
if(vis[u]) continue;
vis[u]=1;
for(register int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(dis[v]>dis[u]+edge[i].dis)
{
dis[v]=dis[u]+edge[i].dis;
q.push(Node{v,dis[v]});
}
}
}
}
注意事项:
第一个节点不要更新vis标记,在循环的时候再判断和更新;
取出优先队列元素的时候也不用清空标记;
自定义Node的时候别写反了……
下饭集锦:
跑最短路,我写的大根堆……
LCA
int anc[N][25];
int fa[N],dep[N];
void dfs(int u)
{
anc[u][0]=fa[u];
for(register int i=1;i<=20;++i) anc[u][i]=anc[anc[u][i-1]][i-1];
for(register int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(v==fa[u]) continue;
fa[v]=u;
dep[v]=dep[u]+1;
dfs(v);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
int delta=dep[x]-dep[y];
for(register int i=0;delta;delta>>=1,++i)
if(delta&1) x=anc[x][i];
if(x==y) return x;
for(register int i=20;anc[x][0]!=anc[y][0];--i)
{
if(anc[x][i]!=anc[y][i])
{
x=anc[x][i];
y=anc[y][i];
}
}
return anc[x][0];
}
注意事项:
预处理后,先保证深度x>=y,然后delta倍增往下跳,注意第一重循环从1开始!
下饭集锦:
for(register int i=0;delta;delta>>=1,++i)写成了for(register int i=0;delta>>=1;++i)
结果就是进入循环的时候就已经除以2了qwq
树的直径
树的直径可以两次dfs很简单地求出来,这儿放一个树形dp的方法,实在没记住就dfs吧
void dfs(int u)
{
sum1=sum2=0;
for(register int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(v==fa[u]) continue;
fa[v]=u;
sum2=max(sum2,dfs(v)+edge[i].dis);
if(sum2>sum1) swap(sum2,sum1);
}
ans=max(ans,sum1+sum2);
return ans;
}
注意事项:
sum2维护的是当前次大值,更新完后如果变成最大值了记得交换一下
下饭集锦:
没有下饭qwq
二分图
int match[N];
bool used[N];
bool dfs(int pos)
{
for(register int i=1;i<=m;++i)
{
if(vis[pos][i]&&!used[i])//这条边存在且没用过
{
used[i]=1;
if(!match[i]||dfs(match[i]))//如果还没匹配或可以重新匹配
{
match[i]=pos;
return true;
}
}
}
return false;
}
注意事项:
对左边的每个点进行搜索前,要清空used数组
下饭集锦:
无
Kruskal
void kruskal()
{
sort(edge+1,edge+m+1,cmp);
for(register int i=1;i<=n;++i) fa[i]=i;
for(register int i=1;i<=m;++i)
{
int fx=find(edge[i].from);
int fy=find(edge[i].to);
if(fx==fy) continue;
fa[fx]=fy;
ans+=edge[i].dis;
if(++num==n-1) break;
}
}
注意事项:
kruskal还有什么值得注意的么……
下饭集锦:
无
数论
线性筛
int prime[N],num;
bool notprime[N];
void Ls(int maxn)
{
for(register int i=2;i<=maxn;++i)
{
if(!notprime[i]) prime[++num]=i;
for(register int j=1;j<=num&&prime[j]*i<=maxn;++j)
{
notprime[prime[j]*i]=1;
if(!(i%prime[j])) break;
}
}
}
注意事项:
j从1开始枚举,乘的时候注意边界
下饭集锦:
没有特判1!注意特判notprime[1]=1啊!!!qwq
乘法逆元
如果mod是质数,可以直接费马小定理求逆元
ll qpow(int n,int k)
{
ll res=1;
while(k)
{
if(k&1) res=(res*n)%mod;
n=(n*n)%mod;
k>>=1;
}
return res;
}
ll inv(int n)
{
return qpow(n,mod-2);
}
当要求很多连续的逆元时,可以线性递推求解(二式是阶乘逆元)
void init()
{
inv[1]=1;
for(register int i=2;i<=n;++i)
inv[i]=inv[mod%i]*(mod-mod/i)%mod;
}
如果不保证mod是质数,但n和mod互质,就可以exgcd求解了
ll x,y;
void exgcd(int a,int b)
{
if(!b) {x=1,y=0;return;}
exgcd(b,a%b);
int z=x;x=y;y=z-a/b*y;
}
int main()
{
read(n);read(mod);
exgcd(n,mod);
printf("%lld
",(x%mod+mod)%mod);
}
注意事项:
线性递推的时候(mod-dfrac{mod}{i})千万不要提mod变成(mod*dfrac{i-1}{i}),不然永远是0(笑);
exgcd记不住就重新推一遍,(x->y , y->x-lfloor{dfrac{a}{b}}
floor *y)
下饭集锦:
我还真把mod给提公约数了,然后逆元全是0……
其他
ST表
int main()
{
read(n);read(m);
for(register int i=1;i<=n;++i) read(Max[i][0]);
Lg[0]=-1;
for(register int i=2;i<=100002;++i) Lg[i]=Lg[i/2]+1;
for(register int j=1;j<=25;++j)
for(register int i=1;i+(1<<j)-1<=n;++i)
Max[i][j]=max(Max[i][j-1],Max[i+(1<<(j-1))][j-1]);
for(register int i=1;i<=m;++i)
{
read(l);read(r);
int LG=Lg[r-l+1];
printf("%d
",max(Max[l][LG],Max[r-(1<<LG)+1][LG]));
}
return 0;
}
注意事项:
没啥好注意的,最多就是判边界的时候小心点吧
下饭集锦:
无
数据结构
树状数组
void add(int pos,int v)
{
while(pos<=n)
{
b[pos]+=v;
pos+=lowbit(pos);
}
}
ll query(int pos)
{
ll res=0;
while(pos)
{
res+=b[pos];
pos-=lowbit(pos);
}
return res;
}
注意事项:
加的时候逐步加lowbit,询问的时候逐步减lowbit(废话)
下饭集锦:
如果树状数组都下饭我就没了啊……
分块
int query(int l,int r,int x)
{
int p=pos[l],q=pos[r],res=0;
if(p==q)
{
for(register int i=l;i<=r;++i) if(a[i]==x) res++;
return res;
}
for(register int i=p+1;i<=q-1;++i) res+=b[i][x];
for(register int i=l;i<=R[p];++i) if(a[i]==x) res++;
for(register int i=L[q];i<=r;++i) if(a[i]==x) res++;
return res;
}
int main()
{
num=sqrt(n);
for(register int i=1;i<=num;++i)
{
L[i]=(i-1)*num+1;
R[i]=i*num;
}
if(R[num]<n) num++,L[num]=R[num-1]+1,R[num]=n;
for(register int i=1;i<=num;++i)
for(register int j=L[i];j<=R[i];++j)
pos[j]=i,b[i][a[j]]++;
}
注意事项:
如果初始分块没有覆盖所有点,把最后几个点也放入块中
下饭集锦:
无