ZCETHAN の 板子们
CSP/NOIP 赛前的模板复习。其中代码一般以洛谷模板题为基础。变量类型一般为 int
,数据范围一般为 \(10^5\),算法范围一般为 提高级 。
不定期更新。
具体还是看代码。
数据结构
倍增表(ST 表)
\(Q\) 次询问长度为 \(n\) 区间内的最大值。
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
using namespace std;
const int MAXN=1e5+10;
int dp[MAXN][20],a[MAXN],n;
void init(){
for(int i=1;i<=n;i++)
dp[i][0]=a[i];
for(int j=1;j<20;j++)
for(int i=1;i+(1<<(j-1))<=n;i++)
dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
int ask(int l,int r){
int k=log2(r-l+1);
return max(dp[l][k],dp[r-(1<<k)+1][k]);
}
int main()
{
scanf("%d",&n);
int Q,l,r;scanf("%d",&Q);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
init();
while(Q--){
scanf("%d%d",&l,&r);
printf("%d\n",ask(l,r));
}
}
线段树
区间加,区间求和。
#include<bits/stdc++.h>
#define ll long long
#define inf 1<<30
using namespace std;
const int MAXN=1e5+10;
struct Tree{
int l,r;
ll sum,inc;
}tr[MAXN<<2];
#define ls i<<1
#define rs i<<1|1
ll a[MAXN];
void build(int i,int l,int r){
tr[i].l=l;tr[i].r=r;tr[i].inc=0;
if(l==r){tr[i].sum=a[l];return;}
int mid=l+r>>1;
build(ls,l,mid);build(rs,mid+1,r);
tr[i].sum=tr[ls].sum+tr[rs].sum;
}
void pushdown(int i){
if(!tr[i].inc) return;
tr[i].sum+=tr[i].inc*(tr[i].r-tr[i].l+1);
tr[ls].inc+=tr[i].inc;
tr[rs].inc+=tr[i].inc;
tr[i].inc=0;
}
void upd(int i,int l,int r,ll v){
if(tr[i].l==l&&tr[i].r==r){
tr[i].inc+=v;return;
}tr[i].sum+=(r-l+1)*v;
int mid=tr[i].l+tr[i].r>>1;
if(r<=mid) upd(ls,l,r,v);
else if(l>mid) upd(rs,l,r,v);
else upd(ls,l,mid,v),upd(rs,mid+1,r,v);
}
ll query(int i,int l,int r){
if(tr[i].l==l&&tr[i].r==r)
return tr[i].sum+tr[i].inc*(r-l+1);
pushdown(i);
int mid=tr[i].l+tr[i].r>>1;
if(r<=mid) return query(ls,l,r);
else if(l>mid) return query(rs,l,r);
else return query(ls,l,mid)+query(rs,mid+1,r);
}
int main()
{
int n,m,x,y,op;ll k;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
build(1,1,n);
while(m--){
scanf("%d%d%d",&op,&x,&y);
if(op==1){
scanf("%lld",&k);
upd(1,x,y,k);
}else printf("%lld\n",query(1,x,y));
}
}
线段树优化建图
优化建图 \([l,r]\to u\),\(u\to [l,r]\),\(u\to v\)。
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pii pair<int,ll>
#define mkp make_pair
#define fi first
#define se second
using namespace std;
const int MAXN=1e5+10;
vector<pii> e[MAXN<<6];
ll dis[MAXN<<6];
int Cnt=0,rk[2][MAXN];
struct tree{int l,r,id;}tr[2][MAXN<<2];
#define ls i<<1
#define rs i<<1|1
void build(int d,int i,int l,int r){
tr[d][i].id=++Cnt;tr[d][i].l=l;tr[d][i].r=r;
if(l==r){rk[d][l]=Cnt;return;}int mid=l+r>>1;
build(d,ls,l,mid);build(d,rs,mid+1,r);
if(d==0) e[tr[d][i].id].push_back(mkp(tr[d][ls].id,0)),
e[tr[d][i].id].push_back(mkp(tr[d][rs].id,0));
else e[tr[d][ls].id].push_back(mkp(tr[d][i].id,0)),
e[tr[d][rs].id].push_back(mkp(tr[d][i].id,0));
}
void ntolr(int i,int l,int r,int nd,ll w){
if(tr[0][i].l==l&&tr[0][i].r==r){
e[rk[1][nd]].push_back(mkp(tr[0][i].id,w));
return;
}int mid=tr[0][i].l+tr[0][i].r>>1;
if(r<=mid) ntolr(ls,l,r,nd,w);
else if(l>mid) ntolr(rs,l,r,nd,w);
else ntolr(ls,l,mid,nd,w),ntolr(rs,mid+1,r,nd,w);
}
void lrton(int i,int l,int r,int nd,ll w){
if(tr[1][i].l==l&&tr[1][i].r==r){
e[tr[1][i].id].push_back(mkp(rk[0][nd],w));
return;
}int mid=tr[1][i].l+tr[1][i].r>>1;
if(r<=mid) lrton(ls,l,r,nd,w);
else if(l>mid) lrton(rs,l,r,nd,w);
else lrton(ls,l,mid,nd,w),lrton(rs,mid+1,r,nd,w);
}
void nton(int fr,int to,ll w){
e[rk[1][fr]].push_back(mkp(rk[0][to],w));
}
int n;
void init(){
build(0,1,1,n);build(1,1,1,n);
for(int i=1;i<=n;i++)
e[rk[0][i]].push_back(mkp(rk[1][i],0));
}
树状数组
单点修改,区间求和。
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
using namespace std;
const int MAXN=5e5+10;
ll tr[MAXN];
int n;
int lbt(int x){return x&(-x);}
void upd(int x,ll v){for(;x<=n;x+=lbt(x))tr[x]+=v;}
ll ask(int x){ll ret=0;for(;x;x-=lbt(x))ret+=tr[x];return ret;}
int main()
{
int m;ll k;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&k),upd(i,k);
int x,y,op;
while(m--){
scanf("%d%d",&op,&x);
if(op==1){
scanf("%lld",&k);
upd(x,k);
}else{
scanf("%d",&y);
printf("%lld\n",ask(y)-ask(x-1));
}
}
return 0;
}
SAM
求出 \(S\) 的所有出现次数不为 \(1\) 的子串的出现次数乘上该子串长度的最大值。
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
using namespace std;
const int MAXN=1e6+10;
int fa[MAXN<<1],ch[MAXN<<1][30],tot=1;
int siz[MAXN<<1],lst=1,len[MAXN<<1];
void ins(int c){
int p=lst,q=++tot;lst=q;
len[q]=len[p]+1;siz[q]=1;
while(p&&!ch[p][c]) ch[p][c]=q,p=fa[p];
if(!p) fa[q]=1;
else{
int x=ch[p][c];
if(len[p]+1==len[x]) fa[q]=x;
else{
int cl=++tot;
fa[cl]=fa[x];
fa[x]=fa[q]=cl;
len[cl]=len[p]+1;
memcpy(ch[cl],ch[x],sizeof(ch[x]));
while(p&&ch[p][c]==x) ch[p][c]=cl,p=fa[p];
}
}
}
char s[MAXN];
vector<int> e[MAXN<<1];
ll ans=0;
void dfs(int x){
for(int s:e[x]){
dfs(s);siz[x]+=siz[s];
}if(siz[x]>1)ans=max(ans,1ll*siz[x]*len[x]);
}
int main()
{
scanf("%s",s+1);
int len=strlen(s+1);
for(int i=1;i<=len;i++)
ins(s[i]-'a');
for(int i=1;i<=tot;i++)
e[fa[i]].push_back(i);
dfs(1);
printf("%lld\n",ans);
return 0;
}
笛卡尔树
构建一颗笛卡尔树。
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
using namespace std;
const int MAXN=1e7+10;
#define gc getchar()
int read(){
int w=0;char c=gc;
while(!isdigit(c))c=gc;
while(isdigit(c))w=w*10+c-'0',c=gc;
return w;
}//tuu
int ls[MAXN],rs[MAXN];
int n,p[MAXN],stk[MAXN],top;
inline void build(){
for(int i=1;i<=n;i++){
while(top&&p[stk[top]]>p[i]) top--;
ls[i]=rs[stk[top]];
rs[stk[top]]=i;
stk[++top]=i;
}
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
p[i]=read();
build();
ll ansl=0,ansr=0;
for(int i=1;i<=n;i++)
ansl^=1ll*i*(ls[i]+1),
ansr^=1ll*i*(rs[i]+1);
printf("%lld %lld\n",ansl,ansr);
return 0;
}
Treap
插入 \(x\) 数
删除 \(x\) 数(若有多个相同的数,因只删除一个)
查询 \(x\) 数的排名(排名定义为比当前数小的数的个数 \(+1\))
查询排名为 \(x\) 的数
求 \(x\) 的前驱(前驱定义为小于 \(x\),且最大的数)
求 \(x\) 的后继(后继定义为大于 \(x\),且最小的数)
#include<bits/stdc++.h>
#define ll long long
#define inf 1e8
using namespace std;
const int MAXN=1e5+10;
struct node{
int ls,rs;
int key,val,siz,cnt;
}tr[MAXN];
int root,idx;
inline void pushup(int p){
tr[p].siz=tr[tr[p].ls].siz+tr[tr[p].rs].siz+tr[p].cnt;
}
int getnode(int key){
tr[++idx].key=key;
tr[idx].val=rand();
tr[idx].cnt=tr[idx].siz=1;
return idx;
}
void zig(int &p){
int q=tr[p].ls;
tr[p].ls=tr[q].rs;tr[q].rs=p;p=q;
pushup(tr[p].rs);pushup(p);
}
void zag(int &p){
int q=tr[p].rs;
tr[p].rs=tr[q].ls;tr[q].ls=p;p=q;
pushup(tr[p].ls);pushup(p);
}
void build(){
getnode(-inf);getnode(inf);
root=1;tr[1].rs=2;
pushup(root);
if(tr[1].val<tr[2].val)
zag(root);
}
void ins(int &p,int key){
if(!p) p=getnode(key);
else if(tr[p].key==key) tr[p].cnt++;
else if(tr[p].key>key){
ins(tr[p].ls,key);
if(tr[tr[p].ls].val>tr[p].val) zig(p);
}else{
ins(tr[p].rs,key);
if(tr[tr[p].rs].val>tr[p].val) zag(p);
}pushup(p);
}
void del(int &p,int key){
if(!p) return;
if(tr[p].key==key){
if(tr[p].cnt>1) tr[p].cnt--;
else if(tr[p].ls||tr[p].rs){
if(!tr[p].rs||tr[tr[p].ls].val>tr[tr[p].rs].val)
zig(p),del(tr[p].rs,key);
else zag(p),del(tr[p].ls,key);
}else p=0;
}
else if(tr[p].key>key) del(tr[p].ls,key);
else del(tr[p].rs,key);
pushup(p);
}
int grbk(int p,int key){
if(!p) return 0;
if(tr[p].key==key) return tr[tr[p].ls].siz+1;
if(tr[p].key>key) return grbk(tr[p].ls,key);
return tr[tr[p].ls].siz+tr[p].cnt+grbk(tr[p].rs,key);
}
int gkbr(int p,int rank){
if(!p) return inf;
if(rank<=tr[tr[p].ls].siz) return gkbr(tr[p].ls,rank);
if(rank<=tr[tr[p].ls].siz+tr[p].cnt) return tr[p].key;
return gkbr(tr[p].rs,rank-tr[tr[p].ls].siz-tr[p].cnt);
}
int prev(int p,int key){
if(!p) return -inf;
if(tr[p].key>=key) return prev(tr[p].ls,key);
else return max(tr[p].key,prev(tr[p].rs,key));
}
int next(int p,int key){
if(!p) return inf;
if(tr[p].key<=key) return next(tr[p].rs,key);
else return min(tr[p].key,next(tr[p].ls,key));
}
int main()
{
build();
int n,op,x;
for(scanf("%d",&n);n--;){
scanf("%d%d",&op,&x);
if(op==1) ins(root,x);
else if(op==2) del(root,x);
else if(op==3) printf("%d\n",grbk(root,x)-1);
else if(op==4) printf("%d\n",gkbr(root,x+1));
else if(op==5) printf("%d\n",prev(root,x));
else printf("%d\n",next(root,x));
}
}
Splay
插入 \(x\) 数
删除 \(x\) 数(若有多个相同的数,因只删除一个)
查询 \(x\) 数的排名(排名定义为比当前数小的数的个数 \(+1\))
查询排名为 \(x\) 的数
求 \(x\) 的前驱(前驱定义为小于 \(x\),且最大的数)
求 \(x\) 的后继(后继定义为大于 \(x\),且最小的数)
强制在线
const int MAXN=2e6+10;
struct Tree{int ch[2],cnt,siz,val,fa;}nd[MAXN];
int root,tot;
void pushup(int rt){
nd[rt].siz=nd[nd[rt].ch[0]].siz+nd[nd[rt].ch[1]].siz+nd[rt].cnt;
}void clear(int rt){
nd[rt].ch[0]=nd[rt].ch[1]=0;
nd[rt].cnt=nd[rt].siz=nd[rt].val=nd[rt].fa=0;
}int chk(int rt){return nd[nd[rt].fa].ch[1]==rt;}
void rot(int rt){
int p=nd[rt].fa,g=nd[p].fa,d=chk(rt);
nd[g].ch[chk(p)]=rt;nd[rt].fa=g;
nd[p].ch[d]=nd[rt].ch[d^1];
nd[nd[rt].ch[d^1]].fa=p;
nd[rt].ch[d^1]=p;nd[p].fa=rt;
pushup(p);pushup(rt);
}
void splay(int rt,int gl){
while(nd[rt].fa!=gl){
int p=nd[rt].fa,g=nd[p].fa;
if(g==gl) rot(rt);
else if(chk(rt)==chk(p)) rot(p),rot(rt);
else rot(rt),rot(rt);
}if(!gl) root=rt;
}
void ins(int rt,int val,int f){
if(!rt){
rt=++tot;nd[rt].val=val;nd[rt].siz=1;nd[rt].cnt=1;
nd[rt].fa=f;nd[f].ch[val>=nd[f].val]=rt;
splay(rt,0);return;
}if(nd[rt].val==val){
nd[rt].cnt++;splay(rt,0);
return;
}
int d=(nd[rt].val<=val);
ins(nd[rt].ch[d],val,rt);
}
int rank(int rt,int val){
int ret=1;
while(rt){
if(nd[rt].val==val){
ret+=nd[nd[rt].ch[0]].siz;
splay(rt,0);return ret;
}else if(val<nd[rt].val) rt=nd[rt].ch[0];
else ret+=nd[rt].cnt+nd[nd[rt].ch[0]].siz,rt=nd[rt].ch[1];
}return ret;
}
int kth(int rt,int k){
while(rt){
int lsiz=nd[nd[rt].ch[0]].siz;
if(k>=lsiz+1&&k<=lsiz+nd[rt].cnt){
splay(rt,0);return nd[rt].val;
}else if(k<=lsiz) rt=nd[rt].ch[0];
else k-=lsiz+nd[rt].cnt,rt=nd[rt].ch[1];
}return nd[rt].val;
}
void del(int rt,int x){
if(nd[rt].val==x){splay(rt,0);
if(nd[rt].cnt>1) nd[rt].cnt--;
else if(nd[rt].ch[0]){
int ls=nd[rt].ch[0];
while(nd[ls].ch[1]) ls=nd[ls].ch[1];
splay(ls,rt);nd[nd[rt].ch[1]].fa=ls;
nd[ls].ch[1]=nd[rt].ch[1];root=ls;
pushup(ls);clear(rt);nd[ls].fa=0;
}else root=nd[rt].ch[1],nd[root].fa=0,clear(rt);
return;
}del(nd[rt].ch[nd[rt].val<x],x);
}
int pre(int val){
int rk=rank(root,val)-1;
return kth(root,rk);
}
int suf(int val){
int rk=rank(root,val+1);
return kth(root,rk);
}
void debug(int rt){
if(nd[rt].ch[0]) debug(nd[rt].ch[0]);
printf("%d %d ",nd[rt].val,nd[rt].cnt);
if(nd[rt].ch[1]) debug(nd[rt].ch[1]);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1,x;i<=n;i++){
scanf("%d",&x);
ins(root,x,0);
}
int opt,x,lst=0,ans=0;
for(int i=1;i<=m;i++){
scanf("%d%d",&opt,&x);
x^=lst;
if(opt==1) ins(root,x,0);
else if(opt==2) del(root,x);
else if(opt==3) lst=rank(root,x),ans^=lst;
else if(opt==4) lst=kth(root,x),ans^=lst;
else if(opt==5) lst=pre(x),ans^=lst;
else lst=suf(x),ans^=lst;
}printf("%d\n",ans);
}
FHQ-Treap
插入 \(x\) 数
删除 \(x\) 数(若有多个相同的数,因只删除一个)
查询 \(x\) 数的排名(排名定义为比当前数小的数的个数 \(+1\))
查询排名为 \(x\) 的数
求 \(x\) 的前驱(前驱定义为小于 \(x\),且最大的数)
求 \(x\) 的后继(后继定义为大于 \(x\),且最小的数)
强制在线
const int MAXN=2e6+10;
mt19937 RAND(time(0));
struct node{int ls,rs,val,rnd,siz;}tr[MAXN];
int tot;
int crt(int val){
tr[++tot].rnd=RAND();tr[tot].val=val;
tr[tot].siz=1; return tot;
}
void pushup(int i){tr[i].siz=tr[tr[i].ls].siz+tr[tr[i].rs].siz+1;}
void split(int i,int val,int &x,int &y){
if(!i){x=y=0;return;}
if(tr[i].val<=val) x=i,split(tr[i].rs,val,tr[i].rs,y);
else y=i,split(tr[i].ls,val,x,tr[i].ls);
pushup(i);
}
int merge(int u,int v){
if(!u||!v) return u|v;
if(tr[u].rnd<tr[v].rnd){
tr[u].rs=merge(tr[u].rs,v);
pushup(u);return u;
}else{
tr[v].ls=merge(u,tr[v].ls);
pushup(v);return v;
}
}
int root;
void ins(int val){
int now=crt(val),a,b;
split(root,val,a,b);
root=merge(merge(a,now),b);
}
void del(int val){
int a,b,c;
split(root,val,a,c);
split(a,val-1,a,b);
b=merge(tr[b].ls,tr[b].rs);
root=merge(merge(a,b),c);
}
int rak(int val){
int a,b;
split(root,val-1,a,b);
int ret=tr[a].siz+1;
root=merge(a,b);
return ret;
}
int kth(int x,int k){
while(x){
if(k==tr[tr[x].ls].siz+1) return tr[x].val;
else if(k<tr[tr[x].ls].siz+1) x=tr[x].ls;
else k-=tr[tr[x].ls].siz+1,x=tr[x].rs;
}return -1;
}
int pre(int val){
return kth(root,rak(val)-1);
}
int suf(int val){
return kth(root,rak(val+1));
}
void debug(int x){
if(!x) return;
debug(tr[x].ls);
printf("%d ",tr[x].val);
debug(tr[x].rs);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1,a;i<=n;i++)
scanf("%d",&a),ins(a);
int opt,x,lst=0,ans=0;
for(int _=1;_<=m;_++){
scanf("%d%d",&opt,&x);
x^=lst;
if(opt==1) ins(x);
else if(opt==2) del(x);
else if(opt==3) lst=rak(x),ans^=lst;
else if(opt==4) lst=kth(root,x),ans^=lst;
else if(opt==5) lst=pre(x),ans^=lst;
else lst=suf(x),ans^=lst;
}printf("%d\n",ans);
}
LCT
struct Tree{int fa,ch[2],sum,val,rev;}tr[MAXN];
#define ls tr[x].ch[0]
#define rs tr[x].ch[1]
void pushup(int x){tr[x].sum=tr[ls].sum^tr[rs].sum^tr[x].val;}
void pushr(int x){swap(ls,rs);tr[x].rev^=1;}
bool nroot(int x){return tr[tr[x].fa].ch[0]==x||tr[tr[x].fa].ch[1]==x;}
void pushdown(int x){
if(tr[x].rev){
if(ls) pushr(ls);if(rs) pushr(rs);
tr[x].rev=0;
}
}
void rot(int x){
int f=tr[x].fa,g=tr[f].fa,d=(x==tr[f].ch[1]),v=tr[x].ch[d^1];
if(nroot(f)) tr[g].ch[f==tr[g].ch[1]]=x;tr[x].fa=g;tr[f].ch[d]=v;
if(v) tr[v].fa=f;tr[f].fa=x;tr[x].ch[d^1]=f;pushup(f);
}
int stk[MAXN],top;
void splay(int x){
top=0;int tmp=x;stk[++top]=tmp;
while(nroot(tmp)) tmp=tr[tmp].fa,stk[++top]=tmp;
while(top) pushdown(stk[top--]);
while(nroot(x)){
int f=tr[x].fa,g=tr[f].fa;
if(nroot(f))
rot(tr[f].ch[1]==x^tr[g].ch[1]==f?x:f);
rot(x);
}pushup(x);
}
void access(int x){for(int s=0;x;s=x,x=tr[x].fa)splay(x),rs=s,pushup(x);}
void makeroot(int x){access(x);splay(x);pushr(x);}
int findroot(int x){access(x);splay(x);while(ls)pushdown(x),x=ls;splay(x);return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void link(int x,int y){
makeroot(x);
if(findroot(y)!=x)
tr[x].fa=y;
}
void cut(int x,int y){
makeroot(x);
if(findroot(y)!=x||tr[y].ch[0]||tr[y].fa!=x)
return;
tr[y].fa=tr[x].ch[1]=0;
}
Tarjan
由于 Tarjan 老爷子非常牛逼,他发明的几个算法大部分都要考,所以这里都放出来吧,也可以对比看看 Tarjan 是多作弊 算法之间的相似与不同。
缩点
vector<int> e[MAXN];
int dfn[MAXN],low[MAXN],tot;
int stk[MAXN],top,col[MAXN],scc;
void Tarjan(int x,int fa){
dfn[x]=low[x]=++tot;
stk[++top]=x;
for(int s:e[x]){
if(s==fa) continue;
if(!dfn[s]) Tarjan(s,x),low[x]=min(low[x],low[s]);
else low[x]=min(low[x],dfn[s]);
}if(low[x]==dfn[x]){
scc++;do{col[stk[top]]=scc;
}while(stk[top--]!=x);
}
}
双连通分量
Dinic 网络流
给出一张有向图,源点和汇点,求最大流。
const int MAXN=5010;
struct Edge{
int to;ll fl,cp; Edge(){}
Edge(int _to,ll _fl,ll _cp){to=_to;fl=_fl;cp=_cp;}
};
vector<Edge> e;
vector<int> head[MAXN];
void add(int u,int v,ll f){
e.push_back(Edge(v,0,f));
head[u].push_back(e.size()-1);
e.push_back(Edge(u,0,0));
head[v].push_back(e.size()-1);
}
int S,T,cur[MAXN],d[MAXN];
bool vis[MAXN];
bool bfs(){
queue<int> q; q.push(S);
memset(vis,0,sizeof(vis));
vis[S]=1;d[S]=0;
while(!q.empty()){
int x=q.front();q.pop();
for(int i:head[x]){
if(e[i].fl>=e[i].cp) continue;
if(vis[e[i].to]) continue;
vis[e[i].to]=1;
d[e[i].to]=d[x]+1;
q.push(e[i].to);
}
}
return vis[T];
}
ll dfs(int x,ll a){
if(x==T||!a) return a;
ll fl=0,f;
for(int&i=cur[x];i<head[x].size()&&a;i++){
int s=head[x][i];
if(d[e[s].to]!=d[x]+1) continue;
if((f=dfs(e[s].to,min(a,e[s].cp-e[s].fl)))<=0)
continue;
e[s].fl+=f;e[s^1].fl-=f;
fl+=f;a-=f;if(!a) break;
}return fl;
}
int main()
{
int n,m,u,v;ll w;
scanf("%d%d%d%d",&n,&m,&S,&T);
for(int i=1;i<=m;i++){
scanf("%d%d%lld",&u,&v,&w);
add(u,v,w);
}
ll ans=0;
while(bfs()){
memset(cur,0,sizeof(cur));
ans+=dfs(S,INF);
}printf("%lld\n",ans);
}
Dinic+spfa 费用流
const int MAXN=5e3+10;
struct Edge{int to,fl,cp,cst;};
vector<Edge> g;
vector<int> e[MAXN];
void add(int u,int v,int w,int c){
g.pb(Edge{v,0,w,c});e[u].pb(g.size()-1);
g.pb(Edge{u,0,0,-c});e[v].pb(g.size()-1);
}
int d[MAXN],cur[MAXN],S,T,vis[MAXN];
bool spfa(){
queue<int> q;
memset(d,0x3f,sizeof(d));
memset(vis,0,sizeof(vis));
q.push(S);d[S]=0;vis[S]=1;
while(!q.empty()){
int x=q.front();q.pop();vis[x]=0;
for(int s:e[x]){
if(g[s].fl>=g[s].cp) continue;
if(d[g[s].to]<=d[x]+g[s].cst) continue;
d[g[s].to]=d[x]+g[s].cst;
if(!vis[g[s].to])
q.push(g[s].to),vis[g[s].to]=1;
}
}return d[T]!=LINF;
}
int tot;
int dfs(int x,int a){
if(x==T||!a) return a;
int fl=0,f;vis[x]=1;
for(int &i=cur[x];i<(int)e[x].size();i++){
int s=e[x][i];
if(vis[g[s].to]) continue;
if(d[g[s].to]!=d[x]+g[s].cst) continue;
if((f=dfs(g[s].to,min(a,g[s].cp-g[s].fl)))==0) continue;
fl+=f,a-=f;g[s].fl+=f;g[s^1].fl-=f;tot+=f*g[s].cst;
}vis[x]=0;return fl;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n,m;cin>>n>>m>>S>>T;
rep(i,1,m){
int u,v,w,c;cin>>u>>v>>w>>c;
add(u,v,w,c);
}
int mfl=0;
while(spfa()){memset(cur,0,sizeof(cur));mfl+=dfs(S,INF);}
cout<<mfl<<' '<<tot<<'\n';
return 0;
}
欧拉路径与欧拉回路
算法
SAM
KMP
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
using namespace std;
const int MAXN=1e6+10;
char s[MAXN],t[MAXN];
int nxt[MAXN],slen,tlen;
void getnxt(){
int k=-1;nxt[0]=-1;
for(int i=1;i<tlen;i++){
while(~k&&t[k+1]!=t[i]) k=nxt[k];
if(t[k+1]==t[i]) k++;
nxt[i]=k;
}
}
int main()
{
scanf("%s%s",s,t);
slen=strlen(s);tlen=strlen(t);
getnxt();
int j=-1;
for(int i=0;i<slen;i++){
while(~j&&t[j+1]!=s[i]) j=nxt[j];
if(t[j+1]==s[i]) j++;
if(j==tlen-1){
printf("%d\n",i+1-tlen+1);
j=nxt[j];
}
}
for(int i=0;i<tlen;i++)
printf("%d ",nxt[i]+1);
return 0;
}
最小生成树
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
using namespace std;
const int MAXN=1e5+10;
struct Edge{
int u,v;ll w;
void input(){
scanf("%d%d",&u,&v);
}bool friend operator<(Edge a,Edge b){
return a.w<b.w;
}
}ed[MAXN];
int f[MAXN];
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
ed[i].input();
sort(ed+1,ed+1+m);
for(int i=1;i<=n;i++)
f[i]=i;
ll ans=0;
for(int i=1;i<=m;i++){
int u=find(ed[i].u),v=find(ed[i].v);
if(u==v) continue;
ans+=ed[i].w;
f[u]=v;
}printf("%lld\n",ans);
}
最短路
堆优化 SPFA
给定有向图和起点,求所有点的最短路。
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
using namespace std;
const int MAXN=1e5+10;
struct Edge{
int to;ll w;Edge(){}
Edge(int _to,ll _w){to=_to;w=_w;}
};vector<Edge> e[MAXN];
struct node{
int x;ll dis;node(){}
node(int _x,ll _dis){x=_x;dis=_dis;}
bool friend operator<(node a,node b){return a.dis>b.dis;}
};priority_queue<node> q;
ll dis[MAXN];
int main()
{
int n,m,st;
scanf("%d%d%d",&n,&m,&st);
for(int i=1;i<=m;i++){
int u,v;ll w;
scanf("%d%d%lld",&u,&v,&w);
e[u].push_back(Edge(v,w));
}
for(int i=1;i<=n;i++)
dis[i]=(1ll<<31)-1;
q.push(node(st,0));
dis[st]=0;
while(!q.empty()){
node cur=q.top();q.pop();
if(dis[cur.x]<cur.dis) continue;
for(auto s:e[cur.x]){
node nxt=node(s.to,s.w+cur.dis);
if(dis[nxt.x]>nxt.dis){
dis[nxt.x]=nxt.dis;
q.push(nxt);
}
}
}for(int i=1;i<=n;i++)
printf("%lld ",dis[i]);
return 0;
}
bellman-ford
判断是否有负环
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
using namespace std;
const int MAXN=1e4+10;
struct Edge{
int u,v;ll w;
Edge(){}
Edge(int _u,int _v,ll _w){
u=_u;v=_v;w=_w;
}
}e[MAXN];
ll dis[MAXN];
int f[MAXN];
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
void solve(){
int n,m,tot=0;scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++){
int u,v;ll w;scanf("%d%d%lld",&u,&v,&w);
if(w>=0) e[++tot]=Edge(u,v,w),e[++tot]=Edge(v,u,w);
else e[++tot]=Edge(u,v,w);
}
memset(dis,0x3f,sizeof(dis));
dis[1]=0;
for(int i=1;i<n;i++)
for(int j=1;j<=tot;j++)
if(dis[e[j].v]>dis[e[j].u]+e[j].w){
dis[e[j].v]=dis[e[j].u]+e[j].w;
if(find(e[j].v)!=find(e[j].u))
f[find(e[j].v)]=find(e[j].u);
}
bool neg=0;
for(int j=1;j<=tot;j++)
if(dis[e[j].v]>dis[e[j].u]+e[j].w){
if(find(e[j].v)!=find(1)||find(e[j].u)!=find(1))
continue;
neg=1;break;
}
puts(neg?"YES":"NO");
}
int main()
{
int T;
for(scanf("%d",&T);T--;)
solve();
return 0;
}
//这里注一下:因洛谷板题需求,要求负环与 1 联通,所以加了个并查集。
Floyd
求两两之间的最短路
int dp[MAXN][MAXN];
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dp[i][j]=min(dp[i][k]+dp[k][j],dp[i][j]);
二分图匈牙利算法
求二分图最大匹配
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
using namespace std;
const int MAXN=510;
int mp[MAXN][MAXN],mat[MAXN];
int n,m,E,vis[MAXN];
bool find(int x){
for(int i=1;i<=m;i++){
if(!mp[x][i]||vis[i]) continue;
vis[i]=1;
if(!mat[i]||find(mat[i])){
mat[i]=x;
return 1;
}
}return 0;
}
int main()
{
scanf("%d%d%d",&n,&m,&E);
for(int i=1,u,v;i<=E;i++){
scanf("%d%d",&u,&v);
mp[u][v]=1;
}
int ans=0;
for(int i=1;i<=n;i++)
memset(vis,0,sizeof(vis)),
ans+=find(i);
printf("%d\n",ans);
return 0;
}
倍增求 LCA
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
using namespace std;
const int MAXN=5e5+10;
int fa[MAXN][20],dep[MAXN];
vector<int> e[MAXN];
void dfs(int x,int fat){
fa[x][0]=fat;
for(int i=1;i<20;i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
for(int s:e[x])
if(s!=fat){
dep[s]=dep[x]+1;
dfs(s,x);
}
}
int LCA(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
int dlt=dep[x]-dep[y];
for(int i=19;i>=0;i--)
if(dlt&(1<<i))
x=fa[x][i];
if(x==y) return x;
for(int i=19;i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int main()
{
int n,m,s;
scanf("%d%d%d",&n,&m,&s);
for(int i=1,u,v;i<n;i++){
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}dfs(s,0);
while(m--){
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",LCA(x,y));
}
return 0;
}
Tarjan
数论
这部分代码比较简单少,但是定理与必要的证明比较重要。
欧拉函数
首先是欧拉定理的公式:
于是我们就有:
ll PHI(ll x){
ll ret=x;
for(int i=1;i<=cnt&&pr[i]*pr[i]<=x;i++){
if(x%pr[i]!=0) continue;
while(x%pr[i]==0) x/=pr[i];
ret=ret/pr[i]*(pr[i]-1);
}
if(x>1) ret=ret/x*(x-1);
return ret;
}
当然前面先筛质数——
所以我们还有一种做法,在线筛的时候就顺便把 \(\varphi(n)\) 给求出来。
考虑线筛的过程,每个合数都只被其最小的质因子 \(p_1\) 标记。我们设这个合数是 \(n\),那么它是在 \(m\times p_1=n\) 的时候筛掉的。那想当然的根据欧拉函数的积性,\(\varphi(n)=\varphi(m)\times \varphi(p_1)=\varphi(m)\times (p_1-1)\)。
但是还需要考虑的是 \(m\%p_1=0\) 的情况。此时,我们发现其实 \(n\) 中的质因子与 \(m\) 是完全相同了。那我们回头看上面代码上面的式子,发现 \(\prod\) 后面的东西是一样的,所以最终只要在 \(\varphi(m)\) 的基础上乘上一个 \(p_1\) 就可以了。
const int MAXN=1e6+10;
int pr[MAXN],p[MAXN],cnt,phi[MAXN];
void init(){
phi[1]=1;
for(int i=2;i<=MAXN-10;i++){
if(!p[i]){phi[i]=i-1;pr[++cnt]=i;}
for(int j=1;j<=cnt&&pr[j]*i<=MAXN-10;j++){
p[i*pr[j]]=1;
if(i%pr[j]) phi[i*pr[j]]=phi[i]*(pr[j]-1);
else{phi[i*pr[j]]=phi[i]*pr[j];break;}
}
}
}
欧拉定理
这个东西,记住就好了。
反正证明看过一遍就忘了。
扩展的应该不考吧?
逆元
乘法逆元,三种求法。
-
用费马小定理,由 \(a^{p-1}\equiv1(\mod p)\) 得到 \(a^{p-2}\equiv\dfrac{1}{a}(\mod p)\),条件是 \(p\) 为质数。
-
用扩展欧几里德求解,设逆元为 \(inv\),则有 \(a\times inv\equiv1(\mod p)\),展开后可以得到 \(inv\times a-k\times p=1\),相当于一个不定方程,可以用扩展欧几里德求出一组可行解。没有限制。
-
用线性递推,首先有 \(inv(1)=1\),然后求 \(i\) 的逆元。首先有 \(p\equiv0(\mod p)\),我们试图把 \(p\) 用 \(i\) 代入得到:
\(k=\left\lfloor\dfrac{p}{i}\right\rfloor\),\(j=p\%i\),那么有 \(p=ki+j\),即 \(ki+j\equiv0(\mod p)\)。此时,我们需要式子中出现 \(inv(i)\),于是我们在式子两边同时乘上一个 \(inv(i)\),得到 \(k+j\times inv(i)\equiv0(\mod p)\Rightarrow j\times inv(i)\equiv-k(\mod p)\)。然后我们发现这个 \(j\) 很难搞,其实不然,容易发现 \(j\) 是小于 \(i\) 的,所以 \(inv(j)\) 是已知的,我们直接把 \(j\) 除过去就可以了。\(inv(i)\equiv-k\times inv(j)(\mod p)\),然后把 \(k\) 和 \(j\) 代入就可以了。
inv[1]=1;
for(int i=2;i<=MAXN-10;i++)
inv[i]=((-(MOD/i)*inv[MOD%i])%MOD+MOD)%MOD;
扩展欧几里德
可以求解形如 \(ax+by=c\) 的不定方程。
原理:\(ax+by=\gcd(a,b)\) 有解,并且可以递归求解。
首先根据欧几里德定理,\(\gcd(a,b)=\gcd(b,a\%b)=\gcd(b,a-b\times\left\lfloor\dfrac{a}{b}\right\rfloor)\)。
所以有 \(ax_1+by_1=bx_2+(a-b\times\left\lfloor\dfrac{a}{b}\right\rfloor)y_2=ay_2+b(x_2-\left\lfloor\dfrac{a}{b}\right\rfloor y_2)\)。
所以我们从上层递归得到 \(x_2\) 和 \(y_2\),直接代入得到当前的 \(x_1\) 和 \(y_1\)。
ll exgcd(ll a,ll b,ll &x,ll &y){
if(b==0){x=1,y=0;return a;}
int r=exgcd(b,a%b,y,x);
y-=a/b*x;return r;
}
注意这样求出来的是最小非负整数解,我们还需要知道通解是 \(x=x_0+\dfrac{kb}{\gcd(a,b)}\),\(y=y_0-\dfrac{ka}{\gcd(a,b)}\)。然后就是由于解的方程是 \(ax+by=\gcd(a,b)\),所以我们需要判断 \(c\) 模上 \(\gcd(a,b)\) 是否等于零,否则无解。
中国剩余定理
有一说一这东西真的会考么(((
但还是背一下板子吧,万一呢。。。
ll CRT(ll n){
ll ans=0,M=1,x,y,mi;
for(int i=1;i<=n;i++) M*=m[i];
for(int i=1;i<=n;i++){
mi=M/m[i];
exgcd(mi,m[i],x,y);
x=(x%m[i]+m[i])%m[i];
ans=(ans+r[i]*mi*x)%M;
}
return (ans%M+M)%M;
}
组合数学
这东西真的没啥板子吧……
首先基础是会快速求阶乘和阶乘的逆元……
void init(){
fac[0]=ifac[0]=1;
for(int i=1;i<=MAXN;i++)
fac[i]=fac[i-1]*i%MOD;
ifac[MAXN]=inv(fac[MAXN]);//暴力O(log)求
for(int i=MAXN-1;i>=1;i--)
ifac[i]=ifac[i+1]*(i+1)%MOD;
}
然后是卡特兰数
ll Cat(int n){return ((C(2*n,n)-C(2*n,n+1))%MOD+MOD)%MOD;}
线性代数
矩乘
矩阵乘法与快速幂,是基于简单递推式和矩阵乘法的结合率产生的一种加速算法。
给出递推式 \(f_i=f_{i-1}+f_{i-3}\)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MOD=1e9+7;
const int siz=3;
struct Matrix{ll a[siz][siz];};
Matrix mul(Matrix m1,Matrix m2){
Matrix ret;
memset(ret.a,0,sizeof(ret.a));
for(int i=0;i<siz;i++)
for(int k=0;k<siz;k++){
if(!m1.a[i][k]) continue;
for(int j=0;j<siz;j++)
ret.a[i][j]=(ret.a[i][j]+m1.a[i][k]*m2.a[k][j]%MOD)%MOD;
}
return ret;
}
Matrix qpow(Matrix m,ll p){
Matrix ret;
memset(ret.a,0,sizeof(ret.a));
for(int i=0;i<siz;i++) ret.a[i][i]=1;
while(p){
if(p&1) ret=mul(ret,m);
m=mul(m,m); p>>=1;
}return ret;
}
void solve(){
ll n;
Matrix x,op;
x.a[0][0]=1;
x.a[1][0]=1;
x.a[2][0]=1;
op.a[0][0]=1;op.a[0][1]=0;op.a[0][2]=1;
op.a[1][0]=1;op.a[1][1]=0;op.a[1][2]=0;
op.a[2][0]=0;op.a[2][1]=1;op.a[2][2]=0;
scanf("%lld",&n);
if(n==1||n==2||n==3){puts("1");return;}
printf("%lld\n",mul(qpow(op,n-3),x).a[0][0]%MOD);
}
int main()
{
int T;
for(scanf("%d",&T);T--;)
solve();
}
高斯消元
上面的高消太长了,下面来个短的,可以判断自由元和无解。(自由元就是如果最后输出的时候分母为 \(0\))
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
using namespace std;
const double eps=1e-18;
const int MAXN=110;
double a[MAXN][MAXN],x[MAXN];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n;cin>>n;
rep(i,1,n) rep(j,1,n+1) cin>>a[i][j];
rep(i,1,n){
int mx=i;rep(j,i+1,n) if(fabs(a[j][i])>fabs(a[mx][i])) mx=j;
rep(j,1,n+1) swap(a[i][j],a[mx][j]);
if(fabs(a[i][i])<=eps){cout<<"No Solution\n";return 0;}
rep(j,1,n) if(j^i){
double tmp=a[j][i]/a[i][i];a[j][i]=0;
rep(k,i+1,n+1) a[j][k]-=tmp*a[i][k];
}
}rep(i,1,n) cout<<fixed<<setprecision(2)<<a[i][n+1]/a[i][i]<<'\n';
return 0;
}
线性基
struct BASE{
int p[35];//数组大小改为元素最大二进制位数
void insert(int x){//有必要记得开 long long
for(int i=30;i>=0;i--){
if(((x>>i)&1)==0) continue;
if(!p[i]){p[i]=x;break;}x^=p[i];
}
}//插入
int qmax(int v){
int ret=v;
for(int i=30;i>=0;i--)
ret=max(ret,ret^p[i]);
return ret;
}//询问线性基中的最大值
BASE friend operator+(BASE A,BASE B){
for(int i=30;i>=0;i--)
if(A.p[i]) B.insert(A.p[i]);
return B;
}//合并两个线性基
bool query(int x){
for(int i=30;i>=0;i--){
if(((x>>i)&1)==0) continue;
if(!p[i]) return 0;x^=p[i];
}return 1;
}//询问一个数是否在线性基中
};