OI各类模板

四边形不等式

定义函数 \(f(l,r)\) 满足四边形不等式为:对于所有 $ l \le l' \le r' \le r $ ,满足 $f(l,r) \ge f(l',r') , f(l,r)+f(l',r') \ge f(l,r')+f(l',r) $
对于状态转移方程 $f(i,j)=min \begin{Bmatrix} f(i,k)+f(k+1,j) \end{Bmatrix}+w(i,j) | i<=k<j $ ,有如下结论:

  1. 若w满足四边形不等式,则f满足四边形不等式
  2. 设s(l,r)表示区间[l,r]取最优值时k的值则:

\[s(l,r-1) \le s(l,r) \le s(l+1,r) \]

要点:整体不小于局部,包含不小于相交,长度减1求范围。

例子:P1880 [NOI1995]石子合并

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
const int oo=1e9+7;
const int maxn=2007;
int a[maxn],sum[maxn];
int n,Fi[maxn][maxn],Fa[maxn][maxn],s[maxn][maxn];
int main()
{
#ifdef local
	freopen("pro.in","r",stdin);
#endif
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		sum[i]=sum[i-1]+a[i]; s[i][i]=i; a[i+n]=a[i];
	}
	for(int i=1+n;i<=n*2;i++)
	{
		sum[i]=sum[i-1]+a[i];
		s[i][i]=i;
	}
	for(int i=n*2-1;i>=1;i--)
		for(int j=i+1;j<=n*2;j++)
		{
			int &pos=s[i][j],&val=Fi[i][j]; val=oo;
			Fa[i][j]=max(Fa[i+1][j],Fa[i][j-1])+(sum[j]-sum[i-1]);
			for(int k=s[i][j-1];k<=s[i+1][j];k++)
			{
				int tmp=Fi[i][k]+Fi[k+1][j]+(sum[j]-sum[i-1]);
				if(tmp<val) val=tmp,pos=k;
			}
		}
	int res_ma=0,res_mi=oo;
	for(int i=1;i<=n;i++)
	{
		res_ma=max(res_ma,Fa[i][i+n-1]);
		res_mi=min(res_mi,Fi[i][i+n-1]);
	}
	printf("%d\n%d\n",res_mi,res_ma);
	return 0;
}

斜率优化

传送门


manacher算法

例子:P3805 【模板】manacher算法

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=51000005;
int n,R[maxn],ans;
char a[maxn],s[maxn<<1];
int main()
{
#ifdef local
	freopen("pro.in","r",stdin);
#endif
	scanf("%s",a); n=strlen(a);
	s[0]=s[1]='#';
	for(int i=0;i<n;i++) s[i*2+2]=a[i],s[i*2+3]='#';
	n=2*n+2;
	int maxright=0,mid=0;
	for(int i=1;i<n;i++)
	{
		if(i<maxright) R[i]=min(R[mid*2-i],R[mid]+mid-i);
		else R[i]=1;
		while(s[i-R[i]]==s[i+R[i]]) ++R[i];
		if(R[i]+i>maxright) { maxright=R[i]+i; mid=i; }
	}
	ans=1;
	for(int i=0;i<n;i++) ans=max(ans,R[i]-1);
	printf("%d\n",ans);
	return 0;
}

KMP 算法

例子:P3375 【模板】KMP字符串匹配
传送门


字符串的最小表示

例子:【模板】字符串的最小表示

代码:

#include<cstdio>
#include<cstring>
const int maxn=1e7+5;
char s[maxn];
int n;
int getmin(char *s)
{
	int i=0,j=1,k=0,t;
	while(i<n&&j<n&&k<n)
	{
		t=s[(i+k)%n]-s[(j+k)%n];
		if(!t) k++;
		else
		{
			if(t>0) i+=k+1; 
			else j+=k+1;
			if(i==j) j++;
			k=0;
		}
	}
	return (i<j?i:j)%n;
}
int main()
{
#ifdef local
	freopen("pro.in","r",stdin);
#endif
	scanf("%s",s); n=strlen(s);
	printf("%d\n",getmin(s));
	return 0;
}

AC自动机

例子:P3808 【模板】AC自动机(简单版)

代码:

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxsize=1e6+5;
const int sigsize=26;
int n; char str[maxsize];
struct ACMachine
{
	int e[maxsize][sigsize],f[maxsize],val[maxsize],last[maxsize],cnt;
	void insert(char *s)
	{
		int n=strlen(s),p=0;
		for(int i=0;i<n;i++)
		{
			if(!e[p][s[i]-'a']) e[p][s[i]-'a']=++cnt;
			p=e[p][s[i]-'a'];
		}
		++val[p];
	}
	void build()
	{
		queue<int> Q;
		for(int c=0;c<sigsize;c++) if(e[0][c]) Q.push(e[0][c]);
		while(Q.size())
		{
			int u=Q.front(),k=f[u]; Q.pop();
			for(int c=0;c<sigsize;c++)
			{
				int &v=e[u][c];
				if(!v) { v=e[k][c]; continue; }
				Q.push(v);
				f[v]=e[k][c]; last[v]=val[f[v]]?f[v]:last[f[v]];
			}
		}
	}
	int query(char *s)
	{
		int n=strlen(s),p=0,res=0;
		for(int i=0;i<n;i++)
		{
			p=e[p][s[i]-'a'];
			res+=val[p]; val[p]=0;
			int v=p;
			while(last[v])
			{
				v=last[v];
				res+=val[v]; val[v]=0;
			}
		}
		return res;
	}
}AC;
int main()
{
#ifdef local
	freopen("pro.in","r",stdin);
#endif
	scanf("%d",&n);
	while(n-->0) scanf("%s",str),AC.insert(str);
	AC.build();
	scanf("%s",str);
	printf("%d\n",AC.query(str));
	return 0;
}

DLX算法

例子:UVA1309 Sudoku

关键代码:

struct DLX
{
	int n,sz;
	int s[maxn];
	int row[maxnode],col[maxnode];
	int U[maxnode],D[maxnode],L[maxnode],R[maxnode];
	int ansd,ans[300];
	void init(int n)
	{
		this->n=n; sz=n+1;
		memset(s,0,sizeof(s));
		for(int i=0;i<=n;i++) { U[i]=i; D[i]=i; L[i]=i-1; R[i]=i+1; }
		L[0]=n; R[n]=0;
	}
	inline void push_back(int r,const vector<int> &cols)
	{
		int first=sz,szc=cols.size();
		for(int i=0;i<szc;i++)
		{
			int c=cols[i];
			L[sz]=sz-1; R[sz]=sz+1; D[sz]=c; U[sz]=U[c];
			D[U[sz]]=sz; U[c]=sz;
			row[sz]=r; col[sz]=c;
			++s[c]; ++sz;
		}
		R[sz-1]=first; L[first]=sz-1;
	}
	#define For(i,A,s) for(int i=A[s];i!=s;i=A[i])
	inline void remove(int c)
	{
		L[R[c]]=L[c]; R[L[c]]=R[c];
		For(i,D,c) For(j,R,i) { U[D[j]]=U[j]; D[U[j]]=D[j]; --s[col[j]]; }
	}
	inline void restore(int c)
	{
		For(i,U,c) For(j,L,i) { ++s[col[j]]; U[D[j]]=j; D[U[j]]=j; }
		L[R[c]]=c; R[L[c]]=c;
	}
	bool dfs(int d)
	{
		if(R[0]==0) { ansd=d; return true; }
		int c=R[0];
		For(i,R,0) if(s[i]<s[c]) c=i;
		remove(c);
		For(i,D,c)
		{
			ans[d]=row[i];
			For(j,R,i) remove(col[j]);
			if(dfs(d+1)) return true;
			For(j,L,i) restore(col[j]);
		}
		restore(c);
		return false;
	}
	bool solve(vector<int> &res)
	{
		res.clear();
		if(!dfs(0)) return false;
		for(int i=0;i<ansd;i++) res.push_back(ans[i]);
		return true;
	}
};

欧拉回路

int cnt=0,res[maxn];
void dfs(int u)
{
	for(int it=G[u];it;it=e[it].next)
		if(e[it].v>=0)//not deleted
		{
			int v=e[it].v;
			du[u]--; du[e[it].v]--;
			e[it].v=-1;
			e[it^1].v=-1;
			dfs(v);
		}
	res[cnt++]=u;
}

while(cnt>0) printf("%d ",res[--cnt]);

矩阵树定理

度数矩阵D:是一个\(N\times N\)的矩阵,其中\(D[i][j]=0 (i \neq j),D[i][i]=\)节点\(i\)的度数。

邻接矩阵A:是一个\(N\times N\)的矩阵,其中\(A[i][j]=\)\(i,j\)之间的边数。

基尔霍夫Kirchhoff矩阵K=D-A

该无向图的生成树的个数等于矩阵K去掉一行一列后的行列式的绝对值。


最小生成树(瓶颈生成树)

例子:P3366 【模板】最小生成树

代码:

Prim:
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=5005;
const int maxm=200005;
struct Edge { int v,w; Edge *next; };
Edge *G[maxn],mem[maxm*2],*ecnt=mem;
inline void AddEdge(int u,int v,int w) { ecnt->v=v; ecnt->w=w; ecnt->next=G[u]; G[u]=ecnt++; }
int n,m,inq,res;
typedef pair<int,int> PII;
#define mkp make_pair
priority_queue<PII,vector<PII>,greater<PII> > Q;
int dis[maxn];
bool vis[maxn];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++)
	{
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		AddEdge(a,b,c); AddEdge(b,a,c);
	}
	memset(dis,127,sizeof(dis));
	dis[1]=0;
	Q.push(mkp(0,1));
	while(!Q.empty()&&inq<n)
	{
		int u=Q.top().second,w=Q.top().first;
		Q.pop();
		if(vis[u]) continue;
		vis[u]=true;
		res+=w;
		inq++;
		for(Edge *it=G[u];it;it=it->next)
			if(it->w<dis[it->v]) dis[it->v]=it->w,Q.push(mkp(dis[it->v],it->v));
	}
	if(inq==n) printf("%d\n",res);
	else printf("orz\n");
	return 0;
}

有向图的强连通分量

例子:UVA12167 Proving Equivalences

关键代码:

vector<int> G[maxn];
int dfn[maxn],lowlink[maxn],sccno[maxn],dfs_cnt,scc_cnt;
stack<int> S;
void dfs(int u)
{
	dfn[u]=lowlink[u]=++dfs_cnt;
	S.push(u);
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i];
		if(!dfn[v])
		{
			dfs(v);
			lowlink[u]=min(lowlink[u],lowlink[v]);
		}
		else if(!sccno[v]) lowlink[u]=min(lowlink[u],dfn[v]);
	}
	if(lowlink[u]==dfn[u])
	{
		scc_cnt++;
		while(true)
		{
			int x=S.top(); S.pop();
			sccno[x]=scc_cnt;
			if(x==u) break;
		}
	}
}

dfs_cnt=0; scc_cnt=0;
memset(sccno,0,sizeof(sccno));
memset(dfn,0,sizeof(dfn));
for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i);

无向图最小环

关键代码:

for(int k=1;k<=n;++k)
{
	for(int i=1;i<k;++i)
		for(int j=1;j<i;++j)
			if((dis[i][j]^oo)&&(G[j][k]^oo)&&(G[k][i]^oo))
				res=min(res,dis[i][j]+G[j][k]+G[k][i]);
	for(int i=1;i<=n;++i)
		for(int j=1;j<i;++j)
		{
			int tmp=dis[i][k]+dis[k][j];
			if(tmp<dis[i][j]) dis[i][j]=dis[j][i]=tmp;
		}
}

AOE网关键路径

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
template<typename T> inline void read(T& t)
{
	t=0; int ch,f=false;
	while(ch=getchar(),!((ch>='0'&&ch<='9')||ch=='-'));
	if(ch=='-') f=true,ch=getchar();
	t=ch^48;
	while(ch=getchar(),ch>='0'&&ch<='9') t=t*10+(ch^48);
	if(f) t=-t;
}
template<typename T,typename... Args> inline void read(T& t,Args&... args) { read(t); read(args...); }
const int maxn=1005;
const int maxm=1000005;
const int oo=0x3f3f3f3f;
struct Edge { int v,w; Edge *next; };
Edge mem[maxm],*G[maxn],*ecnt=mem;
inline void AddEdge(int u,int v,int w) { ecnt->v=v; ecnt->w=w; ecnt->next=G[u]; G[u]=ecnt++; }
int du[maxn],stk1[maxn],top1,stk2[maxn],top2;
int ve[maxn],vl[maxn];
int main()
{
	int n,m,u,v,w,res=0;
	read(n,m);
	while(m-->0)
	{
		read(u,v,w);
		AddEdge(u,v,w);
		++du[v];
	}
	for(int i=1;i<=n;i++) if(!du[i]) stk2[top2++]=stk1[top1++]=i;
	while(top1>0)
	{
		int u=stk1[--top1];
		for(Edge *it=G[u];it;it=it->next)
		{
			ve[it->v]=max(ve[it->v],ve[u]+it->w);
			if(--du[it->v]==0) stk2[top2++]=stk1[top1++]=it->v;
		}
	}
	memset(vl,oo,sizeof(vl));
	vl[stk2[n-1]]=ve[stk2[n-1]];
	while(top2>0)
	{
		int u=stk2[--top2];
		for(Edge *it=G[u];it;it=it->next) vl[u]=min(vl[u],vl[it->v]-it->w);
	}
	for(int i=1;i<=n;i++) for(Edge *it=G[i];it;it=it->next)
		if(ve[i]/*ee*/==vl[it->v]-it->w/*el*/) res+=it->w;
	printf("%d\n",res);
	return 0;
}

后缀数组

概念:

  1. 后缀i:从第i个字符开始的后缀
  2. sa[i]:第i小的后缀
  3. rank[i]:后缀i在sa中的下标
  4. height[i]=LCP(后缀sa[i],后缀sa[i-1])
  5. 后缀j和后缀k的LCP=RMQ(height,rank[j]+1,rank[k])

例子:P3809 【模板】后缀排序

关键代码:

char s[maxn];
int sa[maxn],t[maxn],t2[maxn],c[maxn],n;
int rnk[maxn],height[maxn];
void build_sa(int sig)
{
	int *x=t,*y=t2;
	memset(c,0,sizeof(c));
	for(int i=0;i<n;i++) c[x[i]=s[i]]++;
	for(int i=1;i<sig;i++) c[i]+=c[i-1];
	for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
	for(int k=1;k<=n;k<<=1)
	{
		int p=0;
		for(int i=n-k;i<n;i++) y[p++]=i;
		for(int i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
		
		memset(c,0,sizeof(c));
		for(int i=0;i<n;i++) c[x[y[i]]]++;
		for(int i=1;i<sig;i++) c[i]+=c[i-1];
		for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
		
		swap(x,y);
		p=1; x[sa[0]]=0;
		for(int i=1;i<n;i++)
			x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++);
		if(p>=n) break;
		sig=p;
	}
}
void getHeight()
{
	int i,j,k=0;
	for(i=0;i<n;i++) rnk[sa[i]]=i;
	for(i=0;i<n;i++)
	{
		if(rnk[i]==0) continue;
		if(k) k--;
		j=sa[rnk[i]-1];
		while(i+k<n&&j+k<n&&s[i+k]==s[j+k]) k++;
		height[rnk[i]]=k;
	}
}

点分治

例子:P3806 【模板】点分治1

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=10005;
struct Edge { int v,w; Edge *next; };
Edge mem[maxn*2],*G[maxn],*ecnt=mem;
inline void AddEdge(int u,int v,int w) { ecnt->v=v; ecnt->w=w; ecnt->next=G[u]; G[u]=ecnt++; }
int n,m,k,res,Siz,rt,cnt;
bool vis[maxn];
int f[maxn],sz[maxn],dis[maxn];
void GetRoot(int u,int fa)
{
	f[u]=0; sz[u]=1;
	for(Edge *it=G[u];it;it=it->next)
	{
		int v=it->v;
		if(vis[v]||v==fa) continue;
		GetRoot(v,u);
		f[u]=max(f[u],sz[v]); sz[u]+=sz[v];
	}
	f[u]=max(f[u],Siz-f[u]);
	if(f[u]<f[rt]) rt=u;
}
void GetDis(int u,int fa,int d)
{
	for(Edge *it=G[u];it;it=it->next)
	{
		int v=it->v;
		if(vis[v]||v==fa) continue;
		dis[++cnt]=d+it->w;
		GetDis(v,u,dis[cnt]);
	}
}
int GetAns(int u,int d)
{
	dis[cnt=1]=d;
	GetDis(u,0,d);
	sort(dis+1,dis+1+cnt);
	int l=1,res=0;
	while(l<cnt&&dis[l]+dis[cnt]<k) ++l;
	while(l<cnt&&k-dis[l]>=dis[l])
	{
		auto it=equal_range(dis+l+1,dis+cnt+1,k-dis[l]);
		res+=it.second-it.first;
		++l;
	}
	return res;
}
void dfs(int u)
{
	vis[u]=true; res+=GetAns(u,0);
	for(Edge *it=G[u];it;it=it->next)
	{
		int v=it->v;
		if(vis[v]) continue;
		res-=GetAns(v,it->w);
		Siz=sz[v]; rt=0;
		GetRoot(v,u);
		dfs(v);
	}
}
int main()
{
#ifdef local
	freopen("pro.in","r",stdin);
#endif
	scanf("%d%d",&n,&m);
	int a,b,c;
	for(int i=0;i<n-1;i++)
	{
		scanf("%d%d%d",&a,&b,&c);
		AddEdge(a,b,c); AddEdge(b,a,c);
	}
	while(m-->0)
	{
		scanf("%d",&k);
		res=0; memset(vis,0,sizeof(vis)); Siz=n; sz[0]=1e9+7;
		dfs(1);
		puts(res?"AYE":"NAY");
	}
	return 0;
}

01分数规划

算法模板:

  • 二分法(通用算法,验证解快时使用)
L:=...;R:=...;
Repeat
  Mid:=(L+R)/2;
  For I=1..X do D[i]:=A[i]-Mid*B[i];//根据Mid计算D数组
  if 检查(Mid)成功 then L:=Mid
  else R:=Mid;
Until abs(L-R)<Eps;
  • Dinkelbach算法(求解快时使用)
L:=随便什么东西;
Repeat
  Ans:=L;
  For I=1..X do D[i]:=A[i]-L*B[i];//根据L计算D数组
  检查解并记录;
  p:=0;q:=0;
  for I=每一个元素 do 
	 如果元素I在解中
		begin
		  p:=p+A[i];q:=q+B[i];
		end;
  L:=p/q;//更新解
Until abs(Ans-L)<Eps;

线性筛与积性函数

算法模板:

f[1]=1; memset(isp,true,sizeof(isp));
cnt=0;
for(int i=2;i<=n;i++)
{
	if(isp[i])
	{
		p[cnt++]=i;
		根据定义初始化;//eg. phi[i]=i-1;
	}
	for(int j=0;j<cnt&&i*p[j]<=n;j++)
	{
		isp[i*p[j]]=false;
		if(i%p[j]==0)
		{
			特殊处理;//eg. phi[i*p[j]]=phi[i]*p[j];
			break;
		}
		else f[i*p[j]]=f[i]*f[p[j]];
	}
}

long long 乘法取模

inline LL mmul(LL a, LL b, LL m)
{
	LL d=((long double)a/m*b+0.5);//注意!不加0.5可能会有精度问题!
	LL r=a*b-d*m;
	return r<0?r+m:r;
}

分组背包

伪代码:

for 所有的组k
	for v=V..0
		for 所有的i属于组k
			f[v]=max{f[v],f[v-c[i]]+w[i]}

泛化物品

定义:有一个物品,它消耗i的代价时的收益是G[i]

  • 泛化物品的和:合并两个泛化物品的运算
G[i]=max(G1[j-k]+G2[k]) (C>=j>=k>=0)
  • 泛化物品与普通物品的和:普通背包
  • 泛化物品的并(貌似用不到,暂时跳过)

树形依赖背包(后序遍历优化)

例子:P1064 金明的预算方案

代码:

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
template<typename T> inline void read(T& t)
{
	t=0; int ch,f=false;
	while(ch=getchar(),!((ch>='0'&&ch<='9')||ch=='-'));
	if(ch=='-') f=true,ch=getchar();
	t=ch^48;
	while(ch=getchar(),ch>='0'&&ch<='9') t=t*10+(ch^48);
	if(f) t=-t;
}
template<typename T,typename... Args> inline void read(T& t,Args&... args) { read(t); read(args...); }
const int maxn=65;
const int maxm=32005;
int f[maxn][maxm];
vector<int> son[maxn];
int V[maxn],W[maxn];
int n,m,v,p,q;
int sz[maxn],suf[maxn],cnt;
void dfs(int u)
{
	sz[u]=1;
	for(int i=0;i<son[u].size();i++) { dfs(son[u][i]); sz[u]+=sz[son[u][i]]; }
	suf[++cnt]=u;
}
int main()
{
#ifdef local
	freopen("pro.in","r",stdin);
#endif
	read(m,n);
	for(int i=1;i<=n;i++)
	{
		read(v,p,q);
		V[i]=v*p; W[i]=v; son[q].push_back(i);
	}
	dfs(0);
	for(int i=1;i<=cnt;i++)
	{
		int now=suf[i];
		for(int j=m;j>=0;j--)
			if(j>=W[now]) f[i][j]=max(f[i-sz[now]][j],f[i-1][j-W[now]]+V[now]);
			else f[i][j]=f[i-sz[now]][j];
	}
	printf("%d\n",f[cnt][m]);
	return 0;
}

计算几何:传送门


数论:传送门


Dinic

例子:P3376 【模板】网络最大流

代码:

#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
template<typename T> inline void read(T& t)
{
	t=0; bool f=false; char ch;
	while(ch=getchar(),!((ch>='0'&&ch<='9')||ch=='-'));
	if(ch=='-') f=true,ch=getchar();
	t=ch-'0';
	while(ch=getchar(),ch>='0'&&ch<='9') t=t*10+ch-'0';
	if(f) t=-t;
}
template<typename T,typename... Args> inline void read(T& t,Args&... args) { read(t); read(args...); }
const int maxn=10005;
const int oo=1e9+7;
struct Edge { int from,to,cap,flow; };
struct Dinic
{
	int n,m,s,t;
	vector<Edge> edges;
	vector<int> G[maxn];
	bool vis[maxn];
	int d[maxn],pos[maxn];
	inline void AddEdge(int u,int v,int c)
	{
		edges.push_back((Edge){u,v,c,0});
		edges.push_back((Edge){v,u,0,0});
		m=edges.size();
		G[u].push_back(m-2);
		G[v].push_back(m-1);
	}
	inline bool BFS()
	{
		memset(vis,0,sizeof(vis)); memset(d,0x3f,sizeof(d));
		queue<int> Q;
		Q.push(s); vis[s]=true; d[s]=0;
		while(Q.size())
		{
			int u=Q.front(); Q.pop();
			for(int i=0;i<G[u].size();i++)
			{
				Edge &e=edges[G[u][i]];
				if(!vis[e.to]&&e.cap>e.flow)
				{
					vis[e.to]=true;
					d[e.to]=d[u]+1;
					Q.push(e.to);
				}
			}
		}
		return vis[t];
	}
	inline int DFS(int u,int a)
	{
		if(u==t||a==0) return a;
		int flow=0,f;
		for(int &i=pos[u];i<G[u].size();i++)
		{
			Edge &e=edges[G[u][i]];
			if(d[u]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0)
			{
				e.flow+=f; flow+=f;
				edges[G[u][i]^1].flow-=f; a-=f;
				if(a==0) break;
			}
		}
		return flow;
	}
	int MaxFlow(int s,int t)
	{
		this->s=s; this->t=t;
		int flow=0;
		while(BFS())
		{
			memset(pos,0,sizeof(pos));
			flow+=DFS(s,oo);
		}
		return flow;
	}
}dinic;
int n,m,s,t,u,v,c;
int main()
{
#ifdef local
	freopen("pro.in","r",stdin);
#endif
	read(n,m,s,t);
	while(m-->0)
	{
		read(u,v,c);
		dinic.AddEdge(u,v,c);
	}
	printf("%d\n",dinic.MaxFlow(s,t));
	return 0;
}

快排

#include<cstdio>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int maxn=100005;
int n,a[maxn];
void quicksort(int L,int R)
{
	if(L>=R) return;
	int i=L,j=R;
	swap(a[L],a[L+rand()%(R-L+1)]);
	while(i<j)
	{
		while(j>i&&a[j]>=a[L]) j--;
		while(j>i&&a[i]<=a[L]) i++;
		swap(a[i],i==j?a[L]:a[j]);
	}
	quicksort(L,i-1);
	quicksort(i+1,R);
}
int main()
{
#ifdef local
	freopen("pro.in","r",stdin);
#endif
	scanf("%d",&n);
	for(int i=0;i<n;i++) scanf("%d",&a[i]);
	srand(187);
	quicksort(0,n-1);
	for(int i=0;i<n;i++) printf("%d ",a[i]); puts("");
	return 0;
}

归并排序 & 求逆序对

#include<cstdio>
#include<cstring>
const int maxn=5e5+5;
int n; long long res;
int a[maxn],b[maxn];
void GB(int *a,int *b,int len)
{
	if(len<=1) return;
	int M=len/2,p1=0,p2=M,p=0;
	GB(a,b,M); GB(a+M,b+M,len-M);
	while(p1<M&&p2<len)
	{
		if(a[p1]<=a[p2]) b[p++]=a[p1++];
		else res+=M-p1,b[p++]=a[p2++];
	}
	while(p1<M) b[p++]=a[p1++];
	while(p2<len) b[p++]=a[p2++];
	memcpy(a,b,len<<2);
}
int main()
{
#ifdef local
	freopen("pro.in","r",stdin);
#endif
	scanf("%d",&n);
	for(int i=0;i<n;i++) scanf("%d",&a[i]);
	GB(a,b,n);
	printf("%lld\n",res);
	return 0;
}

基数排序

#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
int n,a[maxn],b[maxn],cnt[10];
void bksort()
{
	int mx=a[0];
	for(int i=1;i<n;i++) mx=max(mx,a[i]);
	for(int exp=1;mx/exp>0;exp*=10)
	{
		for(int i=0;i<10;i++) cnt[i]=0;
		for(int i=0;i<n;i++) cnt[a[i]/exp%10]++;
		for(int i=1;i<10;i++) cnt[i]+=cnt[i-1];
		for(int i=n-1;i>=0;i--) b[--cnt[a[i]/exp%10]]=a[i];
		memcpy(a,b,n<<2);
	}
}
int main()
{
#ifdef local
	freopen("pro.in","r",stdin);
#endif
	scanf("%d",&n);
	for(int i=0;i<n;i++) scanf("%d",&a[i]);
	bksort();
	for(int i=0;i<n;i++) printf("%d ",a[i]); puts("");
	return 0;
}

LCA

例题:P3379 【模板】最近公共祖先(LCA)

Tarjan

#include<cstdio>
template<typename T>
inline void read(T& t)
{
    t=0; bool f=false; char ch;
    while(ch=getchar(),!((ch>='0'&&ch<='9')||ch=='-'));
    if(ch=='-') f=true,ch=getchar();
    t=ch-'0';
    while(ch=getchar(),ch>='0'&&ch<='9') t=t*10+ch-'0';
    if(f) t=-t;
}
template<typename T,typename... Args>
inline void read(T& t,Args&... args)
{
    read(t); read(args...);
}
template<typename T>
inline void write(T t)
{
    if(t==0) { putchar('0'); return; }
    char stk[50]; int top;
    if(t<0) putchar('-'),t=-t;
    while(t>0) stk[top++]=t%10+'0',t/=10;
    while(top>0) putchar(stk[--top]);
}

const int maxn=500005;
const int maxm=500000*2+5;
struct Rec { int to,lca; Rec *next; };
Rec edge[maxm],*E[maxn],*ecnt=edge;
Rec query[maxm],*Q[maxn],*qcnt=query;
inline void AddEdge(Rec **G,Rec* &ecnt,int from,int to)
{ ecnt->to=to; ecnt->next=G[from]; G[from]=ecnt++; }
int n,m,s;
bool vis[maxn];
int fa[maxn];
inline int ff(int x)
{
    int a=x,b;
    while(x!=fa[x]) x=fa[x];
    while(a!=x)
    {
        b=fa[a];
        fa[a]=x;
        a=b;
    }
    return x;
}
void dfs(int u)
{
    fa[u]=u;
    vis[u]=true;
    for(Rec *it=E[u];it;it=it->next)
        if(!vis[it->to])
        {
            dfs(it->to);
            fa[it->to]=u;
        }
    for(Rec *it=Q[u];it;it=it->next)
        if(vis[it->to])
        {
            it->lca=ff(it->to);
            (query+((it-query)^1))->lca=it->lca;
        }
}
int main()
{
    read(n,m,s);
    for(int i=0;i<n-1;i++)
    {
        int a,b; read(a,b);
        AddEdge(E,ecnt,a,b); AddEdge(E,ecnt,b,a);
    }
    for(int i=0;i<m;i++)
    {
        int a,b; read(a,b);
        AddEdge(Q,qcnt,a,b); AddEdge(Q,qcnt,b,a);
    }
    dfs(s);
    for(int i=0;i<m;i++) { write(query[i*2].lca); putchar('\n'); }
    return 0;
}

倍增

#include<cstdio>
#include<vector>
#include<cstdlib>
using namespace std;
const int maxn=500005;
int n,m,s;
vector<int> G[maxn];
int dep[maxn],anc[maxn][30];
void cal(int o,int fa)
{
    dep[o]=dep[anc[o][0]]+1;
    for(int i=1;i<=25;i++) anc[o][i]=anc[anc[o][i-1]][i-1];
    for(int ch:G[o])
        if(ch!=fa)
        {
            anc[ch][0]=o;
            cal(ch,o);
        }
}
inline int getLCA(int a,int b)
{
    if(a==b) return a;
    if(dep[b]>dep[a]) swap(a,b);
    for(int i=25;i>=0;i--)
        if(dep[anc[a][i]]>=dep[b]) a=anc[a][i];
    if(a==b) return a;
    for(int i=25;i>=0;i--)
        if(anc[a][i]!=anc[b][i]) a=anc[a][i],b=anc[b][i];
    return anc[a][0];
}
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    for(int i=0;i<n-1;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        G[x].push_back(y); G[y].push_back(x);
    }
    cal(s,0);
    while(m-->0)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",getLCA(x,y));
    }
    return 0;
}

RMQ

  1. 构造这颗树的欧拉序(每次经过一个节点都要记录下来,包括回退时)
  2. 在(u,v)的欧拉序中第一次出现的位置之间的深度最小的点即为LCA

欧拉序:

vector<int> a;
void dfs(int u)
{
	a.push_back(u);
	for(Edge *it=G[u];it;it=it->nxt)
	{
		dfs(it->v);
		a.push_back(u);
	}
}
posted @ 2019-08-19 21:36  happyZYM  阅读(466)  评论(0编辑  收藏  举报