模板集合

前言 :记录学过的所有模板,有些比较远古还重新打了一遍。总需要留下点什么。按重要程度(主观)排序。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\) 个顶点构成的完全图的圈(简单环)的数量。
image

数论

数论广泛应用于取模运算、质数筛选、方程求解、组合计数、对数求解等场景。知识点很杂。

  • 快速幂

    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;
    }
    
posted @ 2026-07-17 10:23  Emumumu  阅读(16)  评论(0)    收藏  举报