模板集合
前言 :记录学过的所有模板,有些比较远古还重新打了一遍。总需要留下点什么。按重要程度(主观)排序。CTRL+F 查找。很显然,有很多待补充的。(等我学到那再说吧)
树论 (树形结构)
-
线段树 (区间修改)
#include <bits/stdc++.h> #define int long long #define ls (p<<1) #define rs (p<<1|1) #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b)) #define lowbit(x) ((-(x)) & (x)) using namespace std; int n; const int N=1e5+10; struct tree{int l,r,lz,sum;} tr[N*4]; void updatelz(int p,int lz) { tr[p].sum+=(tr[p].r-tr[p].l+1)*lz ; tr[p].lz+=lz ; } void pushdown(int p) { if(tr[p].lz==0) return ; updatelz(ls,tr[p].lz) ;updatelz(rs ,tr[p].lz) ; tr[p].lz=0; } void build (int p,int l,int r) { tr[p]={l,r,0,0}; if(l==r) return ; int mid=(l+r)>>1 ; build(ls, l,mid) ; build(rs,mid+1,r) ; } void modify(int p,int l,int r,int x) { if(tr[p].l>r||tr[p].r<l) return ; if(l<=tr[p].l && tr[p].r<=r) {updatelz(p,x) ;return ;} pushdown(p) ; modify(ls,l,r,x) ; modify(rs,l,r,x) ; tr[p].sum = tr[ls].sum+tr[rs].sum; } int query(int p,int l,int r) { if(tr[p].l>r||tr[p].r<l) return 0 ; if(l<=tr[p].l && tr[p].r<=r) return tr[p].sum ; pushdown(p) ; return query(ls,l,r)+query(rs,l,r) ; } signed main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0) ; int Q; cin>>n>>Q; build(1,1,n); for(int i=1,x;i<=n;i++) {cin >> x; modify(1,i,i,x) ;} while(Q--) { int opt,l,r; cin >> opt>>l>>r; if(opt ==1) {int x; cin>>x;modify(1,l,r,x) ;} else cout<<query(1,l,r)<<"\n" ; } return 0; } -
树状数组
朴素树状数组
int lowbit(int x) {return (-x)&x ;} void update(int i,int x) { for(;i<=n;i+=lowbit(i)) c[i]+=x ; } int getsum(int i ) { int ans = 0 ; for(;i;i-=lowbit(i)) ans+=c[i] ; return ans ; }树状数组求逆序对
sort(t+1,t+1+n); m=unique(t+1,t+1+n)-t-1; for(int i=1;i<=n;i++) { int pos=lower_bound(t+1,t+1+m,a[i])-t ; ans+=getsum(m)-getsum(pos); update(pos,1); }二维树状数组
int lowbit(int x) {return x&(-x);} void update(int x, int y, int v) { for(int i=x;i<=n;i+=lowbit(i)) for(int j=y;j<=m;j+=lowbit(j)) tr[i][j]+=v; } int query(int x, int y) { int ans=0; for(int i=x;i>0;i-=lowbit(i)) for(int j=y;j>0;j-=lowbit(j)) ans+=tr[i][j]; return ans; } int query_range(int x1, int y1, int x2, int y2) { return query(x2,y2)-query(x1-1,y2)-query(x2,y1-1)+query(x1-1,y1-1); } -
LCA
倍增
void dfs(int u,int fa) { dep[u]=dep[fa]+1; f[u][0]=fa; for(int i=1;i<=lg[dep[u]];i++) f[u][i]=f[f[u][i-1]][i-1]; for(auto v:g[u]) { if(v==fa) continue ; dfs(v,u) ; } } int lca(int u,int v){ if(dep[u]<dep[v]) swap(u,v) ; while(dep[u]>dep[v]){ int k=lg[dep[u]-dep[v]]; u=f[u][k]; } if(u==v) return u; for(int k=lg[dep[u]];k>=0;k--) if(f[u][k]!=f[v][k]) {u=f[u][k];v=f[v][k] ;} return f[u][0] ; }Tarjan
void tarjan(int u) { vis[u]=1; for(auto v:g[u]) if(vis[v]==0) {tarjan(v); fa[v]=u;} for(auto v:query[u]) if(vis[v.first]) ans[v.second]=fid(v.first) ; }树链剖分
void dfs1(int u,int f) { dep[u]=dep[f]+1; fa[u]=f; siz[u]=1; for(auto v:g[u]) { if(v==f) continue ; dfs1(v,u) ; siz[u]+=siz[v] ; if(siz[son[u]]<siz[v]) son[u]=v; } } void dfs2(int u,int t) { top[u]=t; number[u]=++sfn ; rev[sfn]=u; if(son[u]==0) return ; dfs2(son[u],t) ; for(auto v:g[u]) { if(v!=son[u]&&v!=fa[u]) dfs2(v,v) ; } } int lca(int u,int v) { while(top[u]!=top[v]) { if(dep[top[u]]>=dep[top[v]]) u=fa[top[u]] ; else v=fa[top[v]] ; } return dep[u]>=dep[v]?v:u; } -
树链剖分
#include <bits/stdc++.h> #define int long long #define ls (p<<1) #define rs (p<<1|1) using namespace std; const int N=5e5+10 ,inf=1e18 ;int n; int dep[N], fa[N],siz[N],son[N],top[N],type[N] ,rev[N],w[N]; vector<int> g[N] ; int sfn=0,mod; struct TreeNode{int sum,lz,l,r; }tr[N<<2]; void updatelz(int p,int lz) { tr[p].sum=(tr[p].sum+(tr[p].r-tr[p].l+1)*lz)%mod; tr[p].lz=(tr[p].lz+lz)%mod; } void pushdown(int p) { if(tr[p].lz) {updatelz(ls,tr[p].lz) ; updatelz(rs,tr[p].lz); tr[p].lz=0;} } void dfs1(int u,int f) { dep[u]=dep[f]+1; fa[u]=f; siz[u]=1; for(auto v:g[u]) { if(v==f) continue ; dfs1(v,u) ; siz[u]+=siz[v] ; if(siz[son[u]]<siz[v]) son[u]=v ; } } void dfs2(int u,int t) { top[u]=t; type[u]=++sfn ; rev[sfn]=u ; if(son[u]==0) return ; dfs2(son[u],t) ; for(auto v:g[u]) if(v!=son[u]&&v!=fa[u]) dfs2(v,v) ; } void build(int p,int l,int r) { tr[p]={0,0,l,r}; if(l==r) return ; int mid=(l+r)>>1; build(ls,l,mid); build(rs,mid+1,r) ; } void modify(int p,int l,int r,int x,int y,int add) { if(x>r||y<l) return ; if(x<=l&&r<=y) {updatelz(p,add); return ;} pushdown(p) ; int mid=(l+r)>>1; modify(ls,l,mid,x,y,add); modify(rs,mid+1,r,x,y,add) ; tr[p].sum=(tr[ls].sum+tr[rs].sum)%mod; } void modifyTree(int u,int v,int add) { add%=mod ; while(top[u]!=top[v]) { if(dep[top[u]]<=dep[top[v]]) swap(u,v) ; modify(1,1,n,type[top[u]],type[u],add) ; u=fa[top[u]] ; } if(dep[u]>dep[v]) swap(u,v) ; modify(1,1,n,type[u],type[v],add); } int querysum(int p,int l,int r,int x,int y) { if(x>r||y<l) return 0; if(x<=l&&r<=y) return tr[p].sum%mod ; pushdown(p) ; int mid=(l+r)/2 ; return (querysum(ls,l,mid,x,y)+querysum(rs,mid+1,r,x,y))%mod ; } int querysumTree(int u,int v) { int sum=0; while(top[u]!=top[v]) { if(dep[top[u]]<=dep[top[v]]) swap(u,v) ; sum=(sum+querysum(1,1,n,type[top[u]],type[u]))%mod ; u=fa[top[u]] ; } if(dep[u]>dep[v]) swap(u,v) ; sum=(sum+querysum(1,1,n,type[u],type[v]))%mod ; return sum ; } int queryson(int x){return querysum(1,1,n,type[x],type[x]+siz[x]-1)%mod ;} void modifyson(int x,int k) {modify(1,1,n,type[x],type[x]+siz[x]-1,k%mod) ;} signed main() { ios::sync_with_stdio(0);cin.tie(0); cout.tie(0) ; int Q,s; cin>>n>>Q>>s>>mod; for(int i=1;i<=n;i++) cin>>w[i] ; for(int i=1,u,v;i<n;i++) {cin>>u>>v; g[u].push_back(v); g[v].push_back(u) ;} dfs1(s,0); dfs2(s,s); build(1,1,n) ; for(int i=1;i<=n;i++) modify(1,1,n,type[i],type[i],w[i]%mod) ; while(Q--) { int opt; cin>>opt; if(opt==1) { int x,y,add; cin>>x>>y>>add ; modifyTree(x,y,add) ; } else if(opt==2) {int x,y; cin>>x>>y;cout<<querysumTree(x,y)<<"\n";} else if(opt==3) {int x,y; cin>>x>>y;modifyson(x,y) ;} else {int x; cin>>x; cout<<queryson(x)<<"\n";} } return 0; } -
扫描线
#include <bits/stdc++.h> #define int long long #define ls (p<<1) #define rs (p<<1|1) #define mid ((l+r)>>1) using namespace std; const int N=1e6+10 ; struct LineNode{ int x,starty,endy,tag; bool operator<(const LineNode &y) const{ return x==y.x?tag>y.tag:x<y.x; } } line[N<<1]; struct TreeNode{int l,r,sum,lz;} tr[N<<3]; int reid[N<<1],to[N<<1]; map<int,int> mp ; void build(int p,int l,int r) { tr[p]={l,r,0,0}; if(l==r) return ; build(ls,l,mid); build(rs,mid+1,r) ; } void pushup(int p,int l,int r) { if(tr[p].lz==0) tr[p].sum=tr[ls].sum+tr[rs].sum ; else tr[p].sum=reid[r+1]-reid[l] ; } void pushdown(int p,int l,int r,int add) { tr[p].lz+=add ; if(tr[p].lz!=0) tr[p].sum=reid[r+1]-reid[l]; else pushup(p,l,r) ; } void modify(int p,int l,int r,int add) { if(l>tr[p].r||r<tr[p].l) return ; if(l<=tr[p].l&&r>=tr[p].r) {pushdown(p,tr[p].l,tr[p].r,add); return ;} modify(ls,l,r,add); modify(rs,l,r,add) ; pushup(p,tr[p].l,tr[p].r) ; } signed main() { ios::sync_with_stdio(0);cin.tie(0); cout.tie(0) ; int n,tot=0; cin>>n; for(int i=1,x1,x2,y1,y2;i<=n;i++) { cin>>x1>>y1>>x2>>y2 ; line[++tot]={x1,y1,y2,1} ; line[++tot]={x2,y1,y2,-1} ; } n=tot; tot=0; for(int i=1;i<=n;i+=2) { to[++tot]=line[i].starty ; to[++tot]=line[i].endy ; } sort(to+1,to+1+tot) ; int cnt=0; for(int i=1;i<=tot;i++) if(!mp[to[i]]) { mp[to[i]]=++cnt; reid[cnt]=to[i] ; } build(1,1,cnt-1) ; sort(line+1,line+1+n) ; int ans=0 ; for(int i=1;i<=n;i++) { if(i!=1) ans+=tr[1].sum*(line[i].x-line[i-1].x) ; modify(1,mp[line[i].starty],mp[line[i].endy]-1,line[i].tag) ; } cout<<ans; return 0; } -
字典树
#include <bits/stdc++.h> //#define int long long using namespace std; const int N=3e6+10,M=70+10; int tire[N][M],cnt[N] ,tot=1; map<string,bool> vised; inline int getpos(char c) { if('a'<=c&&c<='z') return c-'a' ; else if('0'<=c&&c<='9') return (c-'0')+26 ; else return (c-'A')+36 ; } inline bool work(int v,int &p) { if(tire[p][v]==0) return 1; p=tire[p][v] ; return 0; } inline void insert(string s) { int p=1 ; for(auto v:s){ if(work(getpos(v),p)) {tire[p][getpos(v)]=++tot;p=tire[p][getpos(v)] ;} cnt[p]++ ; } } inline int search(string s) { int p=1; for(auto v:s) if(work(getpos(v),p)) return 0 ; return cnt[p] ; } inline void init(){ for(int i=0;i<=tot;i++){ cnt[i]=0; for(int j=0;j<70;j++) tire[i][j]=0; } tot=1; } inline void sovle() { init() ; int n,m; cin>>n>>m ; for(int i=1;i<=n;i++) {string s; cin>>s; insert(s) ;} while(m--) { string s; cin>>s; cout<<search(s)<<"\n" ; } } signed main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0) ; int T; cin>>T; while(T--) sovle() ; return 0; } -
Kruskal重构树 (应该算是树论)
#include <bits/stdc++.h> #define int long long using namespace std; const int N=3e5*2+10; struct EDGE{int u,v,w;}edge[N];int tot=0; void addedge(int u,int v,int w){edge[++tot]={u,v,w};} bool cmp(EDGE x,EDGE y){return x.w>y.w;} int fa[N],w[N],trfa[N],dep[N],lg[N];int stfa[N][20]; int fid(int x) {if(x!=fa[x])fa[x]=fid(fa[x]);return fa[x];} int LCA(int u,int v) { if(dep[u]<dep[v])swap(u,v); while(dep[u]>dep[v]){int k=lg[dep[u]-dep[v]];u=stfa[u][k];} if(u==v) return u; for(int k=lg[dep[u]];k>=0;k--) if(stfa[u][k]!=stfa[v][k]){u=stfa[u][k];v=stfa[v][k];} return trfa[u]; } signed main() { // freopen("P1967_3.in","r",stdin) ; ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); int n,m; cin>>n>>m; for(int i=1,u,v;i<=m;i++){cin>>u>>v>>w[i]; addedge(u,v,w[i]);} sort(edge+1,edge+1+m,cmp); for(int i=2;i<N;i++)lg[i]=lg[i/2]+1; for(int i=1;i<=n+m;i++){trfa[i]=i;fa[i]=i;} for(int i=1;i<=m;i++) { int u=edge[i].u,v=edge[i].v; int fau=fid(u),fav=fid(v); if(fau==fav) continue; n++; w[n]=edge[i].w; trfa[fau]=trfa[fav]=fa[fau]=fa[fav]=n; } for(int i=n;i>=1;i--) dep[i]=dep[trfa[i]]+1; for(int i=1;i<=n;i++) stfa[i][0]=trfa[i]; for(int i=1;i<=19;i++) for(int u=1;u<=n;u++) stfa[u][i]=stfa[stfa[u][i-1]][i-1]; int Q; cin>>Q; while(Q--) { int u,v; cin>>u>>v; if(fid(u)!=fid(v)) cout<<"-1\n"; else cout<<w[LCA(u,v)]<<"\n"; } return 0; }
图论
-
最短路
单源最短路
void dijkstra() { for(int i=0;i<=n;i++) dis[i]=INF; dis[s]=0; pq.push({0,s}); while(!pq.empty()){ auto u=pq.top().second; pq.pop(); if(vis[u]) continue; vis[u]=1; for(auto [v,w]:g[u]) if(dis[v]>dis[u]+w) { dis[v]=dis[u]+w; pq.push({-dis[v],v}); } } }SPFA附SLF优化
void SPFA() { deque<int> q ; for(int i=1;i<=n;i++) dis[i]=inf ; dis[s]=0 ; q.push_back(s) ; inque[s]=1 ; while(!q.empty()) { int u=q.front() ; q.pop_front() ; inque[u]=0 ; for(auto [v,w]:g[u]) if(dis[u]+w<dis[v]){ dis[v]=dis[u]+w ; if(inque[v]==0) { if(!q.empty()&&dis[v]<dis[q.front()]) q.push_front(v) ; else q.push_back(v) ; inque[v]=1 ; } } } }多源最短路
// 现在我已经不用链式前向星了 但是johnson的代码量实在巨大,就不重写了 #include <bits/stdc++.h> //#define int long long using namespace std; const int N = 3005 ,M = 9005; struct edge{int to,next,w;} g1[M] ,g2[M]; int head1[N] ,head2[N] ,tot1,tot2; int n,m; void addedge1(int u,int v,int w) { g1[++tot1] = {v,head1[u] ,w}; head1[u] = tot1; } void addedge2(int u,int v,int w) { g2[++tot2] = {v,head2[u] ,w}; head2[u] = tot2; } int dispfa[N] ,cnt[N],inq[N]; bool spfa(int s) { memset(dispfa,0x3f,sizeof(dispfa)) ; memset(cnt,0,sizeof(cnt)) ; memset(inq,0,sizeof(inq)) ; queue<int> q; dispfa[s] = 0; q.push(s) ; inq[s] =1; while(!q.empty()){ int u =q.front();q.pop(); inq[u] = 0; for(int i=head1[u];i;i=g1[i].next) { int v = g1[i].to , w=g1[i].w ; if(dispfa[u]+w < dispfa[v]) { dispfa[v]= dispfa[u]+w ; cnt[v] = cnt[u] +1 ; if(cnt[v] > n) return 0; if(!inq[v]) { q.push(v) ; inq[v]= 1; } } } }return 1; } bool vis[N] ;int disdij[N] ; void dijstra(int s) { priority_queue<pair<int,int>,vector<pair<int,int>> ,greater<pair<int,int>>> pq; for(int i=1;i <= n ;i++) disdij[i] = 0x3f3f3f3f,vis[i]= 0; disdij[s] = 0 ;pq.push({0,s}) ; while(!pq.empty()){ auto u = pq.top() ;pq.pop() ; if(vis[u.second]) continue ; vis[u.second] = 1; for(int i=head2[u.second];i;i = g2[i].next) { int v =g2[i].to,w =g2[i].w ; if(disdij[v] > disdij[u.second]+w) { disdij[v]= disdij[u.second] +w; pq.push({disdij[v],v}) ; } } } } signed main() { ios::sync_with_stdio(0);cin.tie(0); cout.tie(0) ; cin>>n>>m; for(int i=1,u,v,w;i <= m;i ++) {cin >> u >>v>>w ;addedge1(u,v,w) ;} for(int i=1;i <= n;i ++) addedge1(0,i,0) ; if(!spfa(0)) {cout <<-1; return 0;} for(int u=1;u<=n;u++) { for(int i=head1[u];i;i =g1[i].next) { int v=g1[i].to ; int w=g1[i] .w; addedge2(u,v,w+dispfa[u]-dispfa[v]) ; } } for(int i=1;i<=n;i++) { dijstra(i) ; int ans = 0; for(int j=1;j<=n;j++) { if(disdij[j] == 0x3f3f3f3f) ans+=j*1e9; else ans+=j *(disdij[j]-dispfa[i]+dispfa[j]) ; } cout <<ans<<"\n" ; } return 0 ; } -
最小生成树
#include <bits/stdc++.h> #define int long long using namespace std; const int N=2e5+10 ; int dis[N],fa[N],tot=0 ; struct EDGE{ int u,v,w; bool operator<(EDGE const&x) {return w<x.w;} } edge[N] ; void init(int n) {for(int i=1;i<=n;i++) fa[i]=i ;} void addegde(int u,int v,int w) {edge[++tot]={u,v,w};} int fid(int x) {if(x!=fa[x]) fa[x]=fid(fa[x]); return fa[x]; } bool unio(int x,int y) { int fax=fid(x),fay=fid(y); if(fax!=fay) {fa[fay]=fax;return 1;} return 0; } signed main() { ios::sync_with_stdio(0);cin.tie(0); cout.tie(0) ; int n,m; cin>>n>>m ; init(n) ; for(int i=1,u,v,w;i<=m;i++) {cin>>u>>v>>w ;addegde(u,v,w) ;} sort(edge+1,edge+1+m) ; int ans=0,cnt=0 ; for(int i=1;i<=m;i++) { if(unio(edge[i].u,edge[i].v)) { ans+=edge[i].w ; if((++cnt)==(n-1)) break ; } } cout<<((cnt>=(n-1))?ans:-1) ; return 0; } -
拓扑排序
#include <bits/stdc++.h> #define int long long using namespace std ; const int N=1500+10,inf=-1e18; int dis[N],du[N]; bool vis[N]; vector<pair<int,int> >g[N];int n,m; void toposort() { queue<int> q; for(int i=1;i<=n;i++) {dis[i]=inf;if(du[i]==0) q.push(i) ;} vis[1]=1; dis[1]=0; while(!q.empty()) { int u=q.front(); q.pop(); cout<<u<<" "; for(auto [v,w]:g[u]) { du[v]--; if(du[v]==0) q.push(v); } } } signed main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0) ; cin>>n; for(int i=1,k;i<=n;i++) { while(1) { cin>>k ; if(k==0) break ; g[i].push_back({k,1}) ; du[k]++; } } toposort() ; return 0 ; } -
缩点
void tarjan(int u) { dfn[u]=low[u]=++sfn ; stk.push(u); instk[u]=1; for(auto v:g[u]) { if(dfn[v]==0) { tarjan(v); low[u]=min(low[u],low[v]); } else if(instk[v]) low[u]=min(low[u],dfn[v]) ; } int top; if(dfn[u]==low[u]) do { top=stk.top() ; stk.pop() ; instk[top]=0; sc[top]=u; if(top!=u) a[u]+=a[top] ; } while(top!=u); } //建图 for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i) ; for(int i=1;i<=m;i++) { int uu=sc[u[i]],vv=sc[v[i]] ; if(uu!=vv) { rg[uu].push_back(vv); du[vv]++ ; } } -
割点
#include <bits/stdc++.h> #define int long long using namespace std; const int N=1e5+10 ; vector<int> g[N],split ; int sfn,dfn[N],low[N]; void tarjan(int u,int f) { dfn[u]=low[u]=++sfn ; int totson=0; bool isplit=0; for(int v:g[u]) { if(dfn[v]==0) { tarjan(v,u) ; totson++; low[u]=min(low[u],low[v]) ; if(low[v]>=dfn[u]) isplit=1; } else low[u]=min(low[u],dfn[v]) ; } if((f==0&&totson>=2)||(isplit&&f!=0)) split.push_back(u) ; } signed main() { ios::sync_with_stdio(0);cin.tie(0); cout.tie(0) ; int n,m; cin>>n>>m; for(int i=1,u,v;i<=m;i++) {cin>>u>>v;g[u].push_back(v); g[v].push_back(u);} for(int i=1;i<=n;i++) if(dfn[i]==0) tarjan(i,0) ; cout<<split.size()<<"\n"; sort(split.begin(),split.end()) ; for(int sp:split) cout<<sp<<" "; return 0; } -
点双
#include <bits/stdc++.h> #define int long long using namespace std; const int N=5e5+10; vector<int> g[N] ,ds[N]; stack<int> stk; int sfn,tot,dfn[N],low[N]; void tarjan(int u,int f) { if(g[u].empty()) { ds[++tot].push_back(u); return ; } dfn[u]=low[u]=++sfn; stk.push(u) ; for(auto v:g[u]) { if(dfn[v]!=0) {low[u]=min(low[u],dfn[v]) ; continue;} tarjan(v,u) ; low[u]=min(low[u],low[v]) ; if(low[v]>=dfn[u]) { tot++ ; while(1) { int top=stk.top() ; stk.pop() ; ds[tot].push_back(top) ; if(v==top) break; } ds[tot].push_back(u) ; } } } signed main() { ios::sync_with_stdio(0);cin.tie(0); cout.tie(0) ; int n,m; cin>>n>>m; for(int i=1,u,v;i<=m;i++) { cin>>u>>v; if(u==v) continue ; g[u].push_back(v); g[v].push_back(u) ; } for(int i=1;i<=n;i++) if(dfn[i]==0) tarjan(i,0) ; cout<<tot<<"\n" ; for(int i=1;i<=tot;i++) { cout<<ds[i].size() <<" "; for(auto dsi:ds[i]) cout<<dsi<<" " ; cout<<"\n"; } return 0; } -
边双
#include <bits/stdc++.h> #define int long long using namespace std; const int N=2e6+10 ; vector<pair<int,int>> g[N]; stack<int> st ; vector<int> ng[N] ; bool inst[N]; int dfn[N],low[N],top,sfn,instk[N] ,cntscc,sc[N]; void tarjan(int u,int last) { dfn[u]=low[u]=++sfn ; st.push(u) ; instk[u]=1; for(auto [v,k]:g[u]) { if(k==last) continue ; if(dfn[v]==0){ tarjan(v,k); low[u]=min(low[u],low[v]) ; if(dfn[u]>=low[v]) {ng[u].push_back(v); ng[v].push_back(u); } } else low[u]=min(low[u],dfn[v]) ; } } bool vis[N]; int cnt; vector<int> ans[N] ; void dfs(int u) { vis[u]=1; ans[cnt].push_back(u) ; for(auto v:ng[u]) { if(vis[v]) continue ; dfs(v) ; } } signed main() { ios::sync_with_stdio(0);cin.tie(0); cout.tie(0) ; int n,m; cin>>n>>m; int tot=0; for(int i=1,u,v;i<=m;i++) { cin>>u>>v; if(u==v) continue ; g[u].push_back({v,++tot}) ; g[v].push_back({u,tot}) ; } for(int i=1;i<=n;i++) if(dfn[i]==0) tarjan(i,0) ; for(int i=1;i<=n;i++) if(vis[i]==0) {dfs(i) ;cnt++;} cout<<cnt<<"\n" ; for(int i=0;i<cnt;i++) { cout<<ans[i].size()<<" " ; for(auto ansi:ans[i]) cout<<ansi<<" " ; cout<<"\n"; } return 0; } -
差分约束
#include <bits/stdc++.h> #define int long long using namespace std; const int N=5e3+10,inf=1e18 ;int n,m; bool inque[N]; int dis[N],cnt[N]; vector<pair<int,int> > g[N] ; bool SPFA(int s) { queue<int> q; for(int i=0;i<=n;i++) {dis[i]=inf,cnt[i]=0; inque[i]=0;} dis[s]=0; q.push(s); inque[s]=1; while(!q.empty()) { int u=q.front(); q.pop(); inque[u]=0; for(auto [v,w]:g[u]) if(dis[u]+w<dis[v]) { cnt[v]=cnt[u]+1; if(cnt[v]>n) return 1; dis[v]=dis[u]+w; if(inque[v]==0) { q.push(v); inque[v]=1; } } } return 0; } signed main() { ios::sync_with_stdio(0);cin.tie(0); cout.tie(0) ; cin>>n>>m; for(int i=1,u,v,w;i<=m;i++) {cin>>u>>v>>w ;g[v].push_back({u,w}) ;} for(int i=1;i<=n;i++) g[0].push_back({i,0}) ; if(SPFA(0)) cout<<"NO" ; else for(int i=1;i<=n;i++) cout<<dis[i]<<" "; return 0; } -
欧拉回路
#include <bits/stdc++.h> #define int long long using namespace std; const int N=2e5+10 ; vector<pair<int,int> > g[N]; stack<int> path; int cur[N],in[N],out[N]; bool vis[N]; void dfs(int u) { for(int i=cur[u];i<(int)g[u].size();i=cur[u]) { cur[u]=i+1; if(vis[g[u][i].second]) continue; vis[g[u][i].second]=1; dfs(g[u][i].first) ; } path.push(u); } signed main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int n,m; cin>>n>>m; for(int i=1,u,v;i<=m;i++) { cin>>u>>v; g[u].push_back({v,i}) ; in[v]++; out[u]++; } for(int i=1;i<=n;i++) sort(g[i].begin(), g[i].end()); int cnt1=0,cnt2=0,s=1; for(int i=1;i<=n;i++) { if(out[i]-in[i]==1) { if((++cnt1)==2) {cout<<"No"; return 0;} s=i; } else if(in[i]-out[i]==1) {if((++cnt2)==2) {cout<<"No"; return 0;} } else if(in[i]!=out[i]){cout<<"No" ; return 0;} } dfs(s) ; while(!path.empty()) { cout<<path.top()<<" "; path.pop(); } return 0; } -
二分图最大匹配
#include <bits/stdc++.h> using namespace std ; const int N=5e4+10 ; vector<int> g[N] ; int n,m,e,dfn[N],rec[N] ; bool dfs(int u,int sfn) { if(dfn[u]==sfn) return 0 ; dfn[u]=sfn ; for(auto v:g[u]) if(rec[v]==0||dfs(rec[v],sfn)) { rec[v]=u ; return 1 ; } return 0 ; } signed main() { ios::sync_with_stdio(0) ; cin.tie(0) ; cout.tie(0) ; cin>>n>>m>>e ; for(int i=1,u,v;i<=e;i++) { cin>>u>>v ; g[u].push_back(v) ; } int ans=0 ; for(int i=1;i<=n;i++) if(dfs(i,i)==1) ans++ ; cout<<ans ; return 0 ; }
Trick 设 \(f_x\) 为一个由 \(x\) 个顶点构成的完全图的圈(简单环)的数量。

数论
数论广泛应用于取模运算、质数筛选、方程求解、组合计数、对数求解等场景。知识点很杂。
-
快速幂
int fpow(int a,int b) { int ans=1; while(b) { if(b&1) {ans*=a ;ans%=m ;} b>>=1 ; a=(a*a)%m; }return ans%m ; } -
欧拉筛
void getprime(int n) { no_prime[0]=no_prime[1]=1; for(int i=2;i<=n;i++) { if(no_prime[i]==0) prime.push_back(i) ; for(auto pj:prime) { if(pj*i>n) break ; no_prime[pj*i]=1 ; if(i%pj==1) break ; } } } -
组合数学
预处理
for(int i=0;i<=N;i++) for(int j=0;j<=i;j++) { if(j==0||j==i) c[i][j]=1; else c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod ; }直接计算
int C(int x,int y){ if(x<y||y<0)return 0; return f[x]*inv[y]%mod*inv[x-y]%mod; } -
分解质因数
void getprimefactor(int n){ for(int i=2;i*i<=n;i++){ int cnt=0; while(n%i==0) {cnt++;n/=i;} a[i]+=cnt; } if(n>1) a[n]++ ; } -
扩展欧几里得 (exgcd)
int exgcd(int a,int b,int &x,int &y) { if(b==0) {x=1,y=0 ;return a;} int g=exgcd(b,a%b,x,y) ; int t=x; x=y;y=t-a/b*y ; return g; } void sovle() { int a,b,c; cin >>a>>b>>c ; int g=__gcd(a,b) ; if(c%g!=0) {cout <<"-1\n";return ;} int x,y;exgcd(a,b,x,y) ; int x0=x*c/g,y0=y*c/g;//特解 int xmin=(x0%(b/g)+b/g)%(b/g) ,ymin=(y0%(a/g)+a/g)%(a/g) ;//最小解 if(xmin==0) xmin=b/g; if(ymin==0) ymin=a/g; int xmax=(c-b*ymin)/a, ymax=(c-a*xmin)/b;//最大解 int len=(xmax-xmin)/(b/g)+1 ;//个数 if(xmax<=0&&ymax<=0) {cout <<xmin <<" "<<ymin<<"\n" ;return;} cout <<len<<" "<<xmin<<" "<<ymin <<" "<<xmax<<" "<<ymax<<"\n"; } -
大步小步(BSGS)
\(b^l \equiv n \pmod p\) 给定b,n,p 求l
保证p为质数int bsgs(int a,int b){ a%=mod;b%=mod; if(b==1)return 0; int m=ceil(sqrt(mod)); unordered_map<int,int>has; int t=b; for(int j=0;j<m;j++){ has[t]=j; t=t*a%mod; }//b*(a^j) int mi=fpow(a,m); t=1; //查a^{i*m}是否在map中 for(int i=1;i<=m;i++){ t=t*mi%mod; if(has.count(t)) return i*m-has[t] ; } return -1; }不保证p为质数
int exbsgs(int a,int b,int p){ a%=p;b%=p; if(b==1||p==1) return 0; int d,k=0,aa=1; while(1){ d=__gcd(a,p) ; if(d==1) break; if(b%d!=0) return -1; k++ ;b/=d;p/=d; aa=aa*(a/d)%p; if(aa==b) return k; } int m=ceil(sqrt(p)) ; int t=b; unordered_map<int,int> mp; for(int i=0;i<m;i++) {mp[t]=i; t=t*a%p;} int mi=fpow(a,m,p) ; t=aa ; for(int i=1;i<=m;i++) { t=t*mi%p ; if(mp.count(t)) return i*m-mp[t]+k; } return -1; } -
Lucas定理
给定n ,m ,p\(C_{n + m}^n \bmod p\) 保证 \(p\) 为质数。
int Lucas(int n,int k,int mod) { if(k==0) return 1 ; return C(n%mod,k%mod)*Lucas(n/mod,k/mod,mod)%mod ; } -
欧拉函数
预处理
void getphi(int n) { phi[1]=1; for(int i=2;i<=n;i++) { if(no_prime[i]==0){ prime.push_back(i) ; phi[i]=i-1; } for(int j=0;j<(int)prime.size();j++) { int m=i*prime[j]; if(m>n) break ; no_prime[m]=1; if(i%prime[j]==0){ phi[m]=phi[i]*prime[j] ; break ; }phi[m]=phi[i]*phi[prime[j]] ; } } }直接求
int getphi(int x){ int ans=x; for(int i=2;i*i<=x;i++) { if(x%i==0) { ans=ans/i*(i-1) ; while(x%i==0) x/=i; } } if(x>1) ans=ans/x*(x-1) ; return ans ; } -
错排问题
d[2]=1; d[3]=2; for(int i=4;i<=n;i++) d[i]=(i-1)*(d[i-1]+d[i-2])%mod ; -
威尔逊定理
题目:\(S_n=\Sigma_{k=1}^n\lfloor\frac{(x-1)!+1}{x}-\lfloor\frac{(x-1)!}{x}\rfloor\rfloor\)
定理:$(p-1)! \equiv -1 \pmod{p} $ p为质数
for(int i=1;i<=n;i++) { if(no_prime[i]==0) table[i]=table[i-1]+1; else table[i]=table[i-1] ; }
动态规划
动态规划通过拆分问题、保存子问题最优解避免重复计算,高效解决最优解、计数类问题。
-
背包dp
01背包
完全背包for(int i=1;i<=m;i++) for(int j=t;j>=w[i];j--) dp[j]=max(dp[j] ,dp[j-w[i]]+v[i]);
二进制优化多重背包for(int i=1;i<=m;i++) for(int j=w[i];j<=t;j++) dp[j]=max(dp[j] ,dp[j-w[i]]+v[i]);for(int i=1;i<=n;i++) { int k=1 ; while(s>0) { int kk=min(k,s) ; int nv=v*kk,nw=w*kk ; for(int j=m;j>=nv;j--) dp[j]=max(dp[j],dp[j-nv]+nw) ; s-=kk ; k<<=1 ; } } -
区间dp
for(int len=2;len<=n;len++) { for(int l=1;l+len-1<=n*2;l++) { int r=l+len-1; for(int k=l;k<r;k++) dp[l][r]=max(dp[l][k]+dp[k+1][r]+sum[r]-sum[l-1],dp[l][r]) ; } } -
状压dp
这东西形式太多了,留一个经典题代码
枚举子集:#include <bits/stdc++.h> //#define int long long #define endl "\n" using namespace std; int dp[2][1<<20] ; signed main() { ios::sync_with_stdio(0);cin.tie(0); cout.tie(0) ; int n,m,k;cin>>n>>m>>k;memset(dp,0x3f3f3f3f,sizeof(dp)) ; dp[0][0]=0; for(int i=1;i<=n;i++) { int t=i&1; for(int st=0;st<(1<<m);st++) dp[t][st]=dp[t^1][st] ; int p=0; for(int j=1,x;j<=k;j++) {cin>>x;p|=(1<<(x-1)) ;} for(int ost=0;ost<(1<<m);ost++) { int nst=ost|p ; dp[t][nst]=min(dp[t][nst],dp[t^1][ost]+1) ; } } if(dp[n&1][(1<<m)-1]==0x3f3f3f3f) cout<<-1; else cout<<dp[n&1][(1<<m)-1]; return 0; }for(int sub=st;sub;sub=(sub-1)&st) -
树形dp
树上背包
树的直径#include <bits/stdc++.h> //#define int long long using namespace std; const int N=105 ; int dp[N][N],edge[N]; bool vis[N]; vector<pair<int,int> > g[N] ; void dfs(int u) { vis[u]=1; for(auto v:g[u]) { if(vis[v.first]) continue ; dfs(v.first) ; for(int i=edge[u];i>=0;i--) for(int j=0;j<=edge[v.first];j++) dp[u][i+j+1]=max(dp[u][i]+dp[v.first][j]+v.second,dp[u][i+j+1]) ; edge[u]+=edge[v.first]+1; } } signed main() { ios::sync_with_stdio(0);cin.tie(0); cout.tie(0) ; int n,k; cin>>n>>k ; for(int i=1,u,v,w;i<n;i++) { cin>>u>>v>>w; g[u].push_back({v,w}) ; g[v].push_back({u,w}) ; } dfs(1) ; cout<<dp[1][k] ; return 0; }#include <bits/stdc++.h> #define int long long using namespace std; const int N=1e5+10 ; vector<int> g[N] ; int depmx[N] ,ans; void dfs(int u,int f) { int mx1=0,mx2=0 ; for(auto v:g[u]) { if(v==f) continue ; dfs(v,u) ; depmx[u]=max(depmx[v]+1,depmx[u]) ; if(depmx[v]+1>mx1) {mx2=mx1;mx1=depmx[v]+1; } else if(depmx[v]+1>mx2) mx2=depmx[v]+1 ; } ans=max(ans,mx1+mx2) ; return ; } signed main() { ios::sync_with_stdio(0);cin.tie(0); cout.tie(0) ; int n; cin>>n; for(int i=1,u,v;i<n;i++) { cin>>u>>v; g[u].push_back(v); g[v].push_back(u) ; } dfs(1,0) ; cout<<ans ; return 0; } -
数位dp
这个东西的变形多,但是万变不离其宗 放一个Windy数的代码#include <bits/stdc++.h> #define int long long using namespace std; int dp[20][20][2] ,a[20]; int dfs(int pos,int last,bool lead,bool limit) { if(pos==0) return 1 ; if(!limit&&dp[pos][last][lead]!=-1) return dp[pos][last][lead] ; int len=9,ans=0; if(limit==1) len=a[pos] ; for(int i=0;i<=len;i++) { if(!lead&&(i==last-1||i==last+1||i==last)) continue ; ans+=dfs(pos-1,i,lead&&i==0,limit&&i==len) ; } if(!limit) dp[pos][last][lead]=ans ; return ans; } int change(int x) { int pos=0; while(x) {a[++pos]=x%10 ;x/=10;} return dfs(pos,0,1,1) ; } signed main() { ios::sync_with_stdio(0);cin.tie(0); cout.tie(0) ; int l,r;memset(dp,-1,sizeof(dp)) ; cin>>l>>r;cout<<change(r)-change(l-1); return 0; }

浙公网安备 33010602011771号