四边形不等式
定义函数 \(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 $ ,有如下结论:
- 若w满足四边形不等式,则f满足四边形不等式
- 设s(l,r)表示区间[l,r]取最优值时k的值则:
\[s(l,r-1) \le s(l,r) \le s(l+1,r)
\]
要点:整体不小于局部,包含不小于相交,长度减1求范围。
代码:
#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算法
代码:
#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 算法
字符串的最小表示
例子:【模板】字符串的最小表示
代码:
#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自动机
代码:
#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算法
关键代码:
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去掉一行一列后的行列式的绝对值。
最小生成树(瓶颈生成树)
代码:
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; }
后缀数组
概念:
- 后缀i:从第i个字符开始的后缀
- sa[i]:第i小的后缀
- rank[i]:后缀i在sa中的下标
- height[i]=LCP(后缀sa[i],后缀sa[i-1])
- 后缀j和后缀k的LCP=RMQ(height,rank[j]+1,rank[k])
关键代码:
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; } }
点分治
代码:
#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)
- 泛化物品与普通物品的和:普通背包
- 泛化物品的并(貌似用不到,暂时跳过)
树形依赖背包(后序遍历优化)
代码:
#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
代码:
#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
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
- 构造这颗树的欧拉序(每次经过一个节点都要记录下来,包括回退时)
- 在(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); } }
来源:https://www.cnblogs.com/happyZYM/p/11379879.html