Loading

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

上面放过了

数论

这部分代码比较简单少,但是定理与必要的证明比较重要。

欧拉函数

首先是欧拉定理的公式:

\[\varphi(n)=\prod\varphi(p_i^{k_i})=\prod(1-\dfrac{1}{p_i})\times p_i^{k_i}=n\times \prod(1-\dfrac{1}{p_i}) \]

于是我们就有:

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^{\varphi(m)}\equiv1(\mod m) \]

这个东西,记住就好了。
反正证明看过一遍就忘了。
扩展的应该不考吧?

逆元

乘法逆元,三种求法。

  • 用费马小定理,由 \(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;
	}//询问一个数是否在线性基中
};
posted @ 2021-10-22 20:05  ZCETHAN  阅读(137)  评论(1编辑  收藏  举报