【知识】CSP-S&NOIP模板总结
PS:本文章中大致按照 NOI大纲 顺序编写,如有遗漏的模板,请告知我。
建议打开目录观看。
如需转载请联系我~
---------------------------------------------------------------------------------------------------------------------------------------
CSP-S 算法总结
-
2.2.1 基础知识与编程环境
无
-
2.2.2 C++ 程序设计 2
- set/nultiset
- map/multimap
- deque/priority_queue
- STL
-
2.2.3 数据结构
-
P1886 滑动窗口 /【模板】单调队列
#include <iostream> using namespace std; int n, k, a[1000005]; int q[1000005], h, t; void maxx() { h = 0; t = -1; for (int i = 1; i <= n; ++i) { while (h <= t && a[q[t]] <= a[i]) t--; q[++t] = i; if (q[h] <= i - k) h++; if (i >= k) cout << a[q[h]] << " "; } cout << endl; } void minn() { h = 0; t = -1; for (int i = 1; i <= n; ++i) { while (h <= t && a[q[t]] >= a[i]) t--; q[++t] = i; if (q[h] <= i - k) h++; if (i >= k) cout << a[q[h]] << " "; } cout << endl; } int main() { cin >> n >> k; for (int i = 1; i <= n; ++i) cin >> a[i]; minn(); maxx(); return 0; }
-
P3865 【模板】ST 表
#include <bits/stdc++.h> using namespace std; const int MAXN = 1e6 + 10; int Max[MAXN][21]; int Query(int l, int r) { int k = log2(r - l + 1); return max(Max[l][k], Max[r - (1 << k) + 1][k]); } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int N, M; cin >> N >> M; for (int i = 1; i <= N; i++) cin >> Max[i][0]; for (int j = 1; j <= 21; j++) for (int i = 1; i + (1 << (j - 1)) <= N; i++) Max[i][j] = max(Max[i][j - 1], Max[i + (1 << (j - 1))][j - 1]); for (int i = 1; i <= M; i++) { int l, r; cin >> l >> r; cout << Query(l, r) << '\n'; } return 0; }
-
P3367 【模板】并查集
#include<bits/stdc++.h> using namespace std; int i,j,k,n,m,s,ans,f[10010],p1,p2,p3; //f[i]表示i的集合名 int find(int k){ //路径压缩 if(f[k]==k)return k; return f[k]=find(f[k]); } int main() { cin>>n>>m; for(i=1;i<=n;i++) f[i]=i;//初始化i的老大为自己 for(i=1;i<=m;i++){ cin>>p1>>p2>>p3; if(p1==1) f[find(p2)]=find(p3); //p3打赢了p2 else if(find(p2)==find(p3)) //是否是一伙的 printf("Y\n"); else printf("N\n"); } return 0; }
-
P3374【模板】树状数组 1
#include <bits/stdc++.h> using namespace std; int n,m,tree[2000010]; int lowbit(int k) { return k & -k; } void add(int x,int k) { while(x<=n) { tree[x]+=k; x+=lowbit(x); } } int sum(int x) { int ans=0; while(x!=0) { ans+=tree[x]; x-=lowbit(x); } return ans; } int main() { cin>>n>>m; for(int i=1; i<=n; i++) { int a; cin>>a; add(i,a); } for(int i=1; i<=m; i++) { int a,b,c; cin>>a>>b>>c; if(a==1) add(b,c); if(a==2) cout<<sum(c)-sum(b-1)<<endl; } }
-
P3372 【模板】线段树 1
#include <bits/stdc++.h> using namespace std; #define ll long long #define maxn 1000005 ll a[maxn],w[maxn*4],lzy[maxn*4]; void pushup(int u){w[u]=w[u*2]+w[u*2+1];} void build(int u,int L,int R){ //线段树建立 if(L==R){w[u]=a[L];return;} int M=L+R>>1; build(u*2,L,M); build(u*2+1,M+1,R); pushup(u); } ll query1(int u,int L,int R,int p){ //单点查询 if(L==R) return w[u]; else { int M=L+R>>1; if(M>=p) return query1(u*2,L,M,p); else return query1(u*2+1,M+1,R,p); } } void update1(int u,int L,int R,int p,ll x){ //单点修改 if(L==R) w[u]=x; else{ int M=L+R>>1; if(M>=p) update1(u*2,L,M,p,x); else update1(u*2+1,M+1,R,p,x); pushup(u); } } bool InRange(int L,int R,int l,int r){return ((l<=L)&&(R<=r));} //判断 [L,R] 是否被 [l,r] 包含 bool OutoRange(int L,int R,int l,int r){ return ((L>r)||(R<l));} //判断 [L,R] 是否和 [l,r] 无交 void maketag(int u,int len,int x){ lzy[u]+=x; w[u]+=len*x; } void pushdown(int u,int L,int R){ int M=L+R>>1; maketag(u*2,M-L+1,lzy[u]); maketag(u*2+1,R-M,lzy[u]); lzy[u]=0; } ll query(int u,int L,int R,int l,int r){ if(InRange(L,R,l,r)){return w[u];} else if(!OutoRange(L,R,l,r)){ int M=L+R>>1; pushdown(u,L,R); return query(u*2,L,M,l,r)+query(u*2+1,M+1,R,l,r); } else return 0; } void update(int u,int L,int R,int l,int r,ll x){ if(InRange(L,R,l,r)){maketag(u,R-L+1,x);} else if(!OutoRange(L,R,l,r)){ int M=L+R>>1; pushdown(u,L,R); update(u*2,L,M,l,r,x); update(u*2+1,M+1,R,l,r,x); pushup(u); } } int main(){ ios::sync_with_stdio(false); cin.tie(NULL);cout.tie(NULL); int n,m; cin>>n>>m; for(int i=1;i<=n;i++){cin>>a[i];} build(1,1,n); for(int i=1;i<=m;i++){ int op,x,y; cin>>op; ll k; if(op==1){ cin>>x>>y>>k; update(1,1,n,x,y,k); } else{ cin>>x>>y; cout<<query(1,1,n,x,y)<<endl; } } return 0; }
-
P8306 【模板】字典树
#include<bits/stdc++.h> using namespace std; int T,q,n,t[3000005][65],cnt[3000005],idx; char s[3000005]; int getnum(char x){ if(x>='A'&&x<='Z') return x-'A'; else if(x>='a'&&x<='z') return x-'a'+26; else return x-'0'+52; } void insert(char str[]){ int p=0,len=strlen(str); for(int i=0;i<len;i++){ int c=getnum(str[i]); if(!t[p][c]) t[p][c]=++idx; p=t[p][c]; cnt[p]++; } } int find(char str[]){ int p=0,len=strlen(str); for(int i=0;i<len;i++){ int c=getnum(str[i]); if(!t[p][c]) return 0; p=t[p][c]; } return cnt[p]; } int main(){ scanf("%d",&T); while(T--){ for(int i=0;i<=idx;i++) for(int j=0;j<=122;j++) t[i][j]=0; for(int i=0;i<=idx;i++) cnt[i]=0; idx=0; scanf("%d%d",&n,&q); for(int i=1;i<=n;i++){ scanf("%s",s); insert(s); } for(int i=1;i<=q;i++){ scanf("%s",s); printf("%d\n",find(s)); } } return 0; }
-
P3369 【模板】普通平衡树
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 100010, INF = 1e8; int n; struct Node { int l, r; int key, val; int cnt, size; }tr[N]; int root, idx; void pushup(int p) { tr[p].size = tr[tr[p].l].size + tr[tr[p].r].size + tr[p].cnt; } int get_node(int key) { tr[ ++ idx].key = key; tr[idx].val = rand(); tr[idx].cnt = tr[idx].size = 1; return idx; } void zig(int &p) // 右旋 { int q = tr[p].l; tr[p].l = tr[q].r, tr[q].r = p, p = q; pushup(tr[p].r), pushup(p); } void zag(int &p) // 左旋 { int q = tr[p].r; tr[p].r = tr[q].l, tr[q].l = p, p = q; pushup(tr[p].l), pushup(p); } void build() { get_node(-INF), get_node(INF); root = 1, tr[1].r = 2; pushup(root); if (tr[1].val < tr[2].val) zag(root); } void insert(int &p, int key) { if (!p) p = get_node(key); else if (tr[p].key == key) tr[p].cnt ++ ; else if (tr[p].key > key) { insert(tr[p].l, key); if (tr[tr[p].l].val > tr[p].val) zig(p); } else { insert(tr[p].r, key); if (tr[tr[p].r].val > tr[p].val) zag(p); } pushup(p); } void remove(int &p, int key) { if (!p) return; if (tr[p].key == key) { if (tr[p].cnt > 1) tr[p].cnt -- ; else if (tr[p].l || tr[p].r) { if (!tr[p].r || tr[tr[p].l].val > tr[tr[p].r].val) { zig(p); remove(tr[p].r, key); } else { zag(p); remove(tr[p].l, key); } } else p = 0; } else if (tr[p].key > key) remove(tr[p].l, key); else remove(tr[p].r, key); pushup(p); } int get_rank_by_key(int p, int key) // 通过数值找排名 { if (!p) return 1; // 本题中不会发生此情况 if (tr[p].key == key) return tr[tr[p].l].size + 1; if (tr[p].key > key) return get_rank_by_key(tr[p].l, key); return tr[tr[p].l].size + tr[p].cnt + get_rank_by_key(tr[p].r, key); } int get_key_by_rank(int p, int rank) // 通过排名找数值 { if (!p) return INF; // 本题中不会发生此情况 if (tr[tr[p].l].size >= rank) return get_key_by_rank(tr[p].l, rank); if (tr[tr[p].l].size + tr[p].cnt >= rank) return tr[p].key; return get_key_by_rank(tr[p].r, rank - tr[tr[p].l].size - tr[p].cnt); } int get_prev(int p, int key) // 找到严格小于key的最大数 { if (!p) return -INF; if (tr[p].key >= key) return get_prev(tr[p].l, key); return max(tr[p].key, get_prev(tr[p].r, key)); } int get_next(int p, int key) // 找到严格大于key的最小数 { if (!p) return INF; if (tr[p].key <= key) return get_next(tr[p].r, key); return min(tr[p].key, get_next(tr[p].l, key)); } int main() { build(); scanf("%d", &n); while (n -- ) { int opt, x; scanf("%d%d", &opt, &x); if (opt == 1) insert(root, x); else if (opt == 2) remove(root, x); else if (opt == 3) printf("%d\n", get_rank_by_key(root, x) - 1); else if (opt == 4) printf("%d\n", get_key_by_rank(root, x + 1 )); else if (opt == 5) printf("%d\n", get_prev(root, x)); else printf("%d\n", get_next(root, x)); } return 0; }
-
P3370 【模板】字符串哈希
#include<bits/stdc++.h> using namespace std; set<string> a; int main(){ string p; int n,i; cin>>n; for(i=0;i<n;i++){ cin>>p; a.insert(p); } cout<<a.size()<<endl; return 0; }
-
-
2.2.4 算法
-
离散化
// arr[i] 为初始数组,下标范围为 [1, n] for (int i = 1; i <= n; ++i) // step 1 tmp[i] = arr[i]; std::sort(tmp + 1, tmp + n + 1); // step 2 int len = std::unique(tmp + 1, tmp + n + 1) - (tmp + 1); // step 3 for (int i = 1; i <= n; ++i) // step 4 arr[i] = std::lower_bound(tmp + 1, tmp + len + 1, arr[i]) - tmp; // std::vector<int> arr; std::vector<int> tmp(arr); // tmp 是 arr 的一个副本 std::sort(tmp.begin(), tmp.end()); tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end()); for (int i = 0; i < n; ++i) arr[i] = std::lower_bound(tmp.begin(), tmp.end(), arr[i]) - tmp.begin();
-
分治
-
基数排序
#include <bits/stdc++.h> using namespace std; const int N = 1e6 + 5; int n, a[N], tot; pair<int, int> p[N]; vector<pair<int, int>> G[N]; int main(){ cin >> n; for (int i = 1; i <= n; i++){ cin >> a[i]; p[i].first = a[i] >> 16,p[i].second = a[i] % 65536; } for (int i = 1; i <= n; i++) G[p[i].second].push_back(p[i]); for (int i = 0; i < 65536; i++) for (int j = 0; j < G[i].size(); j++) p[++tot] = G[i][j]; for (int i = 0; i < 65536; i++) G[i].clear(); for (int i = 1; i <= n; i++) G[p[i].first].push_back(p[i]); tot = 0; for (int i = 0; i < 65536; i++) for (int j = 0; j < G[i].size(); j++) p[++tot] = G[i][j]; for (int i = 1; i <= n; i++)cout <<( (p[i].first << 16) | p[i].second )<< " "; return 0; }
-
归并排序
#include <iostream> using namespace std; const int N=1e6+10; int q[N],tmp[N],n; void MergeSort(int q[],int l,int r){ if(l>=r) return ; int mid=(r+l)>>1; MergeSort(q,l,mid);MergeSort(q,mid+1,r); int i=l,j=mid+1,k=0; while(i<=mid&&j<=r){ if(q[i]<=q[j]) tmp[k++]=q[i++]; else tmp[k++]=q[j++]; } while(i<=mid) tmp[k++]=q[i++]; while(j<=r) tmp[k++]=q[j++]; for(int i=l,j=0;i<=r;i++) q[i]=tmp[j++]; } int main(){ cin>>n; for(int i=0;i<n;i++) cin>>q[i]; MergeSort(q,0,n-1); for(int i=0;i<n;i++) cout<<q[i]<<" "; return 0; }
-
P3375 【模板】KMP
#include<iostream> #include<cstring> #define MAXN 1000010 using namespace std; int kmp[MAXN]; int la,lb,j; char a[MAXN],b[MAXN]; int main() { cin>>a+1; cin>>b+1; la=strlen(a+1); lb=strlen(b+1); for (int i=2; i<=lb; i++) { while(j&&b[i]!=b[j+1]) j=kmp[j]; if(b[j+1]==b[i])j++; kmp[i]=j; } j=0; for(int i=1; i<=la; i++) { while(j>0&&b[j+1]!=a[i]) j=kmp[j]; if (b[j+1]==a[i]) j++; if (j==lb) { cout<<i-lb+1<<endl; j=kmp[j]; } } for (int i=1; i<=lb; i++) cout<<kmp[i]<<" "; return 0; }
-
搜索
-
二分图的判定:
int edge[N][N],color[N],n, m; bool dfs(int u, int c){ color[u] = c; for (int i = 1; i <= n; i++) { if (edge[u][i] == 1) { if (color[i] == c) return false; if (!color[i] && !dfs(i, -c)) return false; } } return true; }
-
P3386 【模板】二分图最大匹配
#include <iostream> #include <cstring> using namespace std; const int N = 10005, M = 200005; int h[N], ne[M], cnt, e[M]; int match[N],vis[N]; int ans; void add(int a,int b){ e[++cnt] = b; ne[cnt] = h[a]; h[a] = cnt; } bool dfs(int x){ for (int i = h[x]; i;i=ne[i]){ int to = e[i]; if(vis[to]) continue; vis[to] = 1; if(match[to]==0||dfs(match[to])){ match[to] = x; return true; } } return false; } int main(){ int n, m, e; cin >> n >> m >> e; for (int i = 1; i <= e;i++){ int u, v; cin >> u >> v; add(u, v + n); add(v + n, u); } for (int i = 1; i <= n;i++){ memset(vis, 0, sizeof(vis)); if(dfs(i)) ans++; } cout << ans << endl; return 0; }
-
P7771 【模板】欧拉路径
#include <bits/stdc++.h> using namespace std; const int N = 100005; int n, m, u, v, d[N], din[N],dout[N]; stack<int> st; vector<int> G[N]; void dfs(int now){ for (int i = d[now]; i < G[now].size(); i = d[now]){ d[now] = i + 1; dfs(G[now][i]); } st.push(now); } int main(){ cin >> n >> m; for (int i = 1; i <= m; i++){ cin >> u >> v; G[u].push_back(v); dout[u]++; din[v]++; } for (int i = 1; i <= n; i++) sort(G[i].begin(), G[i].end()); int S = 1, cnt[2] = {0, 0}; bool flag = 1; for (int i = 1; i <= n; i++){ if (dout[i]!= din[i]) { flag = 0; if (dout[i] - din[i] == 1) cnt[1]++, S = i; else if (din[i] - dout[i] == 1) cnt[0]++; else return puts("No"), 0; } } if (!flag && !(cnt[0] == cnt[1] && cnt[0] == 1)) return puts("No"), 0; dfs(S); while (!st.empty()) cout << st.top()<<" ",st.pop(); return 0; }
-
P8436【模板】边双连通分量
#include <iostream> #include <cstring> #include <vector> using namespace std; const int N = 500010, M = 2000010 * 2; int n, m; int h[N], e[M], ne[M], cnt; int dfn[N], low[N], timestamp; int stk[N], top; int id[N], dcc_cnt; bool is_bridge[M]; int tot[N]; vector<int> ans[N]; void add(int a, int b){ e[++cnt] = b; ne[cnt] = h[a]; h[a] = cnt; } void tarjan(int u, int from){ dfn[u] = low[u] = ++timestamp; stk[++top] = u; for (int i = h[u]; ~i; i = ne[i]){ int j = e[i]; if (!dfn[j]){ tarjan(j, i); low[u] = min(low[u], low[j]); if (low[j] > dfn[u]) is_bridge[i] = is_bridge[i ^ 1] = true; } else if (i != (1 ^ from)) low[u] = min(low[u], dfn[j]); } if (dfn[u] == low[u]){ dcc_cnt++; int y; do{ y = stk[top--]; id[y] = dcc_cnt; } while (y != u); } return; } int main(){ cin >> n >> m; for (int i = 1; i <= m; i++){ int a, b; cin >> a >> b; add(a, b); add(b, a); } for (int i = 1; i <= n; i++) if (!dfn[i]) tarjan(i, -1); cout << dcc_cnt << endl; for (int i = 1; i <= n; i++){ tot[id[i]]++; ans[id[i]].push_back(i); } int p = 1; while (tot[p]){ cout << tot[p] << ' '; for (int i = 0; i < ans[p].size(); i++) cout << ans[p][i] << ' '; p++; cout << endl; } return 0; }
-
P8435【模板】点双连通分量
#include <bits/stdc++.h> using namespace std; const int N = 5e5 + 5, M = 4e6 + 5; int cnt = 1, fir[N], nxt[M], to[M]; int s[M], top, bcc, low[N], dfn[N], idx, n, m; vector<int> ans[N]; inline void tarjan(int u, int fa){ int son = 0; low[u] = dfn[u] = ++idx; s[++top] = u; for (int i = fir[u]; i; i = nxt[i]){ int v = to[i]; if (!dfn[v]){ son++; tarjan(v, u); low[u] = min(low[u], low[v]); if (low[v] >= dfn[u]) { bcc++; while (s[top + 1] != v) ans[bcc].push_back(s[top--]); // 将子树出栈 ans[bcc].push_back(u); // 把割点/树根也丢到点双里 } } else if (v != fa) low[u] = min(low[u], dfn[v]); } if (fa == 0 && son == 0) ans[++bcc].push_back(u); // 特判独立点 } inline void add(int u, int v){ to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt; } int main() { cin >> n >> m; for (int i = 1; i <= m; i++){ int u, v; cin >>u >> v; add(u, v); add(v, u); } for (int i = 1; i <= n; i++){ if (dfn[i]) continue; top = 0; tarjan(i, 0); } cout << bcc << endl; for (int i = 1; i <= bcc; i++){ cout << ans[i].size() << " "; for (int j : ans[i]) cout << j << " "; cout << endl; } return 0; }
-
DAG拓扑排序:
#include <iostream> using namespace std; const int N=100005; int du[N], h[N], e[N], ne[N],cnt; int n; int q[N]; void add(int a,int b){ e[++cnt] = b; ne[cnt] = h[a]; h[a] = cnt; } void topo(){ int hh = 0, tt = -1; for (int i = 1; i <= n;i++) if(!du[i]) q[++tt] = i; while(hh<=tt){ int u = q[hh++]; for (int i = h[u]; i;i=ne[i]){ if(!--du[e[i]]) q[++tt] = e[i]; } } for (int i = 0; i <= tt;i++) cout << q[i] << " "; } int main(){ cin >> n; for (int i = 1; i <= n;i++){ int a; while(cin>>a && a){ add(i, a); du[a]++; } } topo(); return 0; }
-
Dijkstra \(O((n+m)\times log(n+m))\)
#include <bits/stdc++.h> using namespace std; const int N = 200005; int n, m, s; vector<pair<int, int>> v[N]; int dis[N]; bool vis[N]; struct node{ int d, id; bool friend operator<(node xx, node yy){ return xx.d > yy.d; } }; priority_queue<node> Q; void dij(){ for (int i = 1; i <= n; i++) dis[i] = 2e9; dis[s] = 0; Q.push({0, s}); while (!Q.empty()){ int d = Q.top().d; int id = Q.top().id; Q.pop(); if (d > dis[id]) continue; if(vis[id]) continue; vis[id]=1; for (int i = 0; i < v[id].size(); i++){ int to = v[id][i].first; if (dis[to] > dis[id] + v[id][i].second){ dis[to] = dis[id] + v[id][i].second; Q.push({dis[to], to}); } } } } int main(){ cin >> n >> m >> s; for (int i = 1; i <= m; i++){ int x, y, c; cin >> x >> y >> c; v[x].push_back({y, c}); } dij(); for (int i = 1; i <= n; i++) cout << dis[i] << " "; }
-
SPFA:
#include <bits/stdc++.h> using namespace std; queue<int> Q; const int N = 100005, INF = 0x3f3f3f3f; int dis[N]; bool vis[N]; int n, m, s,head[N], pos; struct edge{ int to, next, c; } e[N]; void add(int a, int b, int c){ e[++pos].to = b, e[pos].c = c, e[pos].next = head[a], head[a] = pos; } void spfa(){ for (int i = 1; i <= n; i++) dis[i] = INF, vis[i] = 0; dis[s] = 0, vis[s] = 1, Q.push(s); while (!Q.empty()){ int u = Q.front(); Q.pop(); vis[u] = 0; for (int i = head[u]; i; i = e[i].next){ int v = e[i].to; if (dis[v] > dis[u] + e[i].c){ dis[v] = dis[u] + e[i].c; if (!vis[v]) vis[v] = 1, Q.push(v); } } } } int main(){ cin >> n >> m >> s; for (int i = 1; i <= m; i++){ int x, y, c; cin >> x >> y >> c; add(x, y, c); add(y, x, c); } spfa(); }
- #### Floyd: ```cpp for (k = 1; k <= n; k++) for (x = 1; x <= n; x++) for (y = 1; y <= n; y++) f[x][y] = min(f[x][y], f[x][k] + f[k][y]);
-
Bellman-Ford:
int dis[N][N]; //dis[k][v];表示选取前k个时到达i的最短距离 struct Edge{ int u, v, w; }edge[N]; int n, m; void Bellman_Ford(int s){ memset(dis, INF, sizeof(dis)); for (int i = 1; i <= n; i++) dis[i][s] = 0; for (int k = 1; k <= n - 1; k++) for (int i = 0; i < m; i++){ int u = edge[i].u, v = edge[i].v, w = edge[i].w; dis[k][v] = min(dis[k][v], dis[k - 1][u] + w); } }
-
Kosaraju:
#include<bits/stdc++.h> #define N 200020 using namespace std; int n, m; int head[N], pos; struct edge { to, next, c; }e[N << 1]; void add(int a, int b, int c){ e[++pos].to = b, e[pos].next = head[a], head[a] = pos; e[pos].c = c; } int tot, scc; int st[N], top; bool vis[N]; void dfs1(int u){ vis[u] = 1; for (int i = head[u]; i; i = e[i].next){ int v = e[i].to; if (vis[v] || e[i].c == 0)continue; dfs1(v); } st[++top] = u; } int bel[N]; void dfs2(int u, int bl){ bel[u] = bl, vis[u] = 1; for (int i = head[u]; i; i = e[i].next){ int v = e[i].to; if (vis[v] || e[i].c)continue; dfs2(v, bl); } } int main(){ cin >> n >> m; for (int i = 1; i <= m; i++){ int x, y; cin >> x >> y; add(x, y, 1), add(y, x, 0); } for (int i = 1; i <= n; i++) if (!vis[i])dfs1(i); for (int i = 1; i <= n; i++) vis[i] = 0; for (int i = top; i; i--) if (!vis[st[i]]) { ++scc; dfs2(st[i], scc); } cout << scc << endl; //scc是强连通分量的数量 }
-
Tarjan(强连通分量):
#include<bits/stdc++.h> using namespace std; int n,m,cnt,cntb; vector<int> edge[10001]; vector<int> belong[10001]; bool instack[10001]; int dfn[10001]; int low[10001]; stack<int> s; void Tarjan(int u){ ++cnt; dfn[u]=low[u]=cnt; s.push(u); instack[u]=true; for(int i=0;i<edge[u].size();++i){ int v=edge[u][i]; if(!dfn[v]){ Tarjan(v); low[u]=min(low[u],low[v]); } else if(instack[v]) low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]){ ++cntb; int node; do{ node=s.top(); s.pop(); instack[node]=false; belong[cntb].push_back(node); }while(node!=u); } } int main(){ cin>>n>>m; for(int i=1;i<=m;++i){ int u,v; cin>>u>>v; edge[u].push_back(v); } Tarjan(1); return 0; }
-
Tarjan:割点
#include <bits/stdc++.h> using namespace std; constexpr int N = 1e5 + 5; int n, m, num,root; int dn, dfn[N], low[N], cnt, buc[N],sum; vector<int> e[N]; void Tarjan(int x){ dfn[x]=low[x]=++num; int flag=0; for(int i=0;i<e[x].size();i++){ int to=e[x][i]; if(!dfn[to]){ Tarjan(to); low[x]=min(low[x],low[to]); if(low[to]>=dfn[x]){ flag++; if(x!=root||(flag>=2)){ buc[x]=1; } } } else low[x]=min(low[x],dfn[to]); } } int main() { cin >> n >> m; for(int i = 1; i <= m; i++) { int u, v; cin >> u >> v; e[u].push_back(v); e[v].push_back(u); } for(int i=1;i<=n;i++){ if(!dfn[i]){ root=i; Tarjan(i); } } for(int i=1;i<=n;i++){ if(buc[i]) sum++; } cout<<sum<<endl; for(int i=1;i<=n;i++){ if(buc[i]) cout<<i<<" "; } return 0; }
-
Tarjan: 割边
#include<iostream> #include<cmath> using namespace std; int n, m, root, a, b, total; int e[101][101], dfn[101], low[101], flag[101], head[101]; struct node{ int to; int next; }edge[10010]; int cnt = 1; void add(int u, int v) { edge[cnt].to = v; edge[cnt].next = head[u]; head[u] = cnt++; } void tarjan(int u, int father) { int child = 0; dfn[u] = low[u] = ++total; for (int i = head[u]; i != 0; i = edge[i].next) { int v = edge[i].to; if (!dfn[v]) { child++; tarjan(v, u); low[u] = min(low[u], low[v]); if (low[v] > dfn[u]) { cout << u << "->" << v << endl; } } else if (v != father) { low[u] = min(low[u], dfn[v]); } } } int main() { cin >> n >> m; for (int i = 0; i < m; i++) { cin >> a >> b; add(a, b); add(b, a); } root = 1; tarjan(1, root); for (int i = 1; i <= n; i++) { if(flag[i]) { cout << i << " "; } } return 0; }
-
差分约束:
#include <cstring> #include <iostream> #include <queue> #include <algorithm> using namespace std; struct edge { int v,w,next; } e[10005]; int head[5005],tot[5005],dis[5005],vis[5005],cnt,n,m; void addedge(int u,int v,int w) { e[++cnt].v=v; e[cnt].w=w; e[cnt].next=head[u]; head[u]=cnt; } bool spfa(int s) { queue<int> q; memset(dis,63,sizeof(dis)); dis[s]=0,vis[s]=1; q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); vis[u]=0; for(int i=head[u]; i; i=e[i].next) { int v=e[i].v; if(dis[v]>dis[u]+e[i].w) { dis[v]=dis[u]+e[i].w; if(!vis[v]) { vis[v]=1,tot[v]++; if(tot[v]==n+1)return false; // 注意添加了一个超级源点 q.push(v); } } } } return true; } int main() { cin>>n>>m; for(int i=1; i<=n; i++) addedge(0,i,0); for(int i=1; i<=m; i++) { int v,u,w; cin>>v>>u>>w; addedge(u,v,w); } if(!spfa(0))cout<<"NO"<<endl; else for(int i=1; i<=n; i++) cout<<dis[i]<<' '; return 0; }
-
严格单元次短路:
#include <bits/stdc++.h> using namespace std; const int MAXN = 200010; struct edge{ int to,nxt,v; }e[MAXN]; int h[MAXN],cnt,dis[2][MAXN],n,m; void add(int u,int v,int w){ e[++cnt].nxt=h[u]; e[cnt].to=v; e[cnt].v=w; h[u]=cnt; } struct node{ int pos,dis; friend bool operator<(node a,node b){ return a.dis>b.dis; } }tmp; priority_queue<node> q; void dij(){ for(int i=1;i<=n;i++){ dis[0][i]=dis[1][i]=2147483647; } dis[0][1]=0; tmp.dis=0,tmp.pos=1; q.push(tmp); while(!q.empty()){ tmp=q.top(); q.pop(); int u=tmp.pos,d=tmp.dis; if(d>dis[1][u]) continue; for(int i=h[u];i;i=e[i].nxt){ int v=e[i].to; int w=e[i].v; if(dis[0][v]>d+w){ dis[1][v]=dis[0][v]; tmp.dis=dis[0][v]=d+w; tmp.pos=v; q.push(tmp); } if(dis[1][v]>d+w&&dis[0][v]<d+w){ tmp.dis=dis[1][v]=d+w; tmp.pos=v; q.push(tmp); } } } } int main(){ cin>>n>>m; for(int i=1;i<=m;i++){ int a,b,c; cin>>a>>b>>c; add(a,b,c); add(b,a,c); } dij(); cout<<dis[1][n]; }
-
-
严格次小生成树:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 100010; const ll INF = 1e18; // 定义一个很大的数,用来表示无限大 int n, m, d[N]; // n是点数,m是边数,d[N]是深度数组 bool vis[N]; // vis数组用于标记节点是否被访问过 ll f[N][25], g1[N][25], g2[N][25], mst, ans = INF; // f[N][25]用于存储每个节点的祖先,g1[N][25]存储到祖先路径中的最大边权,g2[N][25]存储到祖先路径中的次大边权 struct Node { ll to, cost; // 节点to和到该节点的边权cost }; vector<Node> v[N]; // 邻接表,用于存储树的结构 // 深度优先搜索(DFS)用于构建树的深度、祖先及边权数组 void dfs(const int x) { vis[x] = true; // 标记当前节点已访问 for (int i = 0; i < v[x].size(); i++) { int y = v[x][i].to; // 获取相邻节点y if (vis[y]) continue; // 如果y已经被访问,跳过 d[y] = d[x] + 1; // 更新节点y的深度 f[y][0] = x; // 更新节点y的第一个祖先为x g1[y][0] = v[x][i].cost; // 更新边权数组g1[y][0] g2[y][0] = -INF; // 初始次大边权设为-INF dfs(y); // 递归访问节点y } } // 预处理,用于填充每个节点的祖先、最大边权、次大边权 inline void prework() { for (int i = 1; i <= 20; i++) for (int j = 1; j <= n; j++) { f[j][i] = f[f[j][i - 1]][i - 1]; // 动态规划计算j节点的第i个祖先 g1[j][i] = max(g1[j][i - 1], g1[f[j][i - 1]][i - 1]); // 计算最大边权 g2[j][i] = max(g2[j][i - 1], g2[f[j][i - 1]][i - 1]); // 计算次大边权 if (g1[j][i - 1] > g1[f[j][i - 1]][i - 1]) g2[j][i] = max(g2[j][i], g1[f[j][i - 1]][i - 1]); else if (g1[j][i - 1] < g1[f[j][i - 1]][i - 1]) g2[j][i] = max(g2[j][i], g1[j][i - 1]); } } // 找出节点x和y的最低公共祖先(LCA),并更新次小生成树的结果 inline void LCA(int x, int y, const ll w) { ll zui = -INF, ci = -INF; // zui存储路径中最大边权,ci存储次大边权 if (d[x] > d[y]) swap(x, y); // 保证x的深度小于或等于y的深度 // 使y节点上升到与x同样深度 for (int i = 20; i >= 0; i--) if (d[f[y][i]] >= d[x]) { zui = max(zui, g1[y][i]); ci = max(ci, g2[y][i]); y = f[y][i]; } if (x == y) { // 如果x和y是同一节点 if (zui != w) ans = min(ans, mst - zui + w); // 更新答案为当前次小生成树 else if (ci != w && ci > 0) ans = min(ans, mst - ci + w); // 如果次大边权符合条件,更新答案 return; } // 同时让x和y向上走,直到找到公共祖先 for (int i = 20; i >= 0; i--) if (f[x][i] != f[y][i]) { zui = max(zui, max(g1[x][i], g1[y][i])); ci = max(ci, max(g2[x][i], g2[y][i])); x = f[x][i]; y = f[y][i]; } // 最后一步,x和y必然是各自的第一个祖先 zui = max(zui, max(g1[x][0], g1[y][0])); if (g1[x][0] != zui) ci = max(ci, g1[x][0]); if (g2[y][0] != zui) ci = max(ci, g2[y][0]); if (zui != w) ans = min(ans, mst - zui + w); // 更新次小生成树 else if (ci != w && ci > 0) ans = min(ans, mst - ci + w); // 更新次小生成树 } // 边的结构体 struct Edge { int from, to; ll cost; bool is_tree; // 标记这条边是否在生成树中 } edge[N * 3]; // 边的比较函数,用于排序 bool operator < (const Edge x, const Edge y) { return x.cost < y.cost; } int fa[N]; // 并查集数组,用于Kruskal算法 // 并查集的查找函数,带路径压缩 inline int find(const int x) { if (fa[x] == x) return x; else return fa[x] = find(fa[x]); } // Kruskal算法,用于求最小生成树 inline void Kruskal() { sort(edge, edge + m); // 将所有边按权值排序 for (int i = 1; i <= n; i++) fa[i] = i; // 初始化并查集 for (int i = 0; i < m; i++) { int x = edge[i].from; int y = edge[i].to; ll z = edge[i].cost; int a = find(x), b = find(y); if (a == b) continue; // 如果x和y已经连通,跳过 fa[find(x)] = y; // 将x和y所在的连通分量合并 mst += z; // 更新最小生成树的总权值 edge[i].is_tree = true; // 标记这条边属于生成树 // 将这条边加入生成树 v[x].push_back((Node){y, z}); v[y].push_back((Node){x, z}); } } int main() { ios_base::sync_with_stdio(false); cin.tie(NULL); cin >> n >> m; // 读入所有边的信息 for (int i = 0, x, y; i < m; i++) { ll z; cin >> x >> y >> z; if (x == y) continue; // 自环边无意义,跳过 edge[i].from = x; edge[i].to = y; edge[i].cost = z; } Kruskal(); // 计算最小生成树 d[1] = 1; // 将根节点的深度设为1 dfs(1); // 深度优先搜索,建立树的结构 prework(); // 预处理,计算祖先和边权信息 // 计算次小生成树 for (int i = 0; i < m; i++) if (!edge[i].is_tree) LCA(edge[i].from, edge[i].to, edge[i].cost); cout << ans << "\n"; // 输出结果 return 0; }
-
Kruskal:
#include <bits/stdc++.h> using namespace std; const int N=2000200; int n,m,f[N]; long long ans=0; struct edge{ int u,v,c; }e[N]; bool operator<(edge x,edge y){ return x.c<y.c; } int find(int x){ return x==f[x]?x:f[x]=find(f[x]); } void sovle(){ cin>>n>>m; for(int i=1;i<=m;i++){ cin>>e[i].u>>e[i].v>>e[i].c; } sort(e+1,e+m+1); for(int i=1;i<=n;i++) f[i]=i; for(int i=1;i<=m;i++){ int fu=find(e[i].u),fv=find(e[i].v); if(fu==fv) continue; f[fu]=fv; ans+=e[i].c; } cout<<ans<<endl; } signed main(){ sovle(); return 0; }
-
Prim:
#include <bits/stdc++.h> using namespace std; const int maxn = 5010; const int inf = 0x3f3f3f3f; int g[maxn][maxn]; int dis[maxn]; bool vis[maxn]; int n, m, u, v, w,cnt; int prime() { int tot = 0; memset(dis, inf, sizeof(dis)); memset(vis, false, sizeof(vis)); dis[1] = 0; for (int i = 1; i <= n; ++i) { int minv = inf, u = -1; for (int j = 1; j <= n; ++j) { if (!vis[j] && minv > dis[j]) { minv = dis[j]; u = j; } } if (u == -1) return -1; vis[u] = true; tot += minv; cnt++; for (int j = 1; j <= n; ++j) { if (!vis[j] && dis[j] > g[u][j]) { dis[j] = g[u][j]; } } } return tot; } int main() { memset(g, inf, sizeof(g)); cin >> n >> m; for (int i = 0; i < m; ++i) { cin >> u >> v >> w; g[u][v] = g[v][u] = min(g[u][v], w); } int tmp=prime(); if(cnt>=n-1) cout << tmp << endl; return 0; }
-
树的直径:
#include <bits/stdc++.h> #define DEBUG(x) std::cerr << #x << '=' << x << std::endl using namespace std; int dp[100010]={0},ans; vector<int> G[100010]; void dfs(int rt,int fa){ dp[rt]=0; int mx=0,mn=0; for(int i=0;i<G[rt].size();i++){ int to=G[rt][i]; if(to==fa) continue; dfs(to,rt); if(dp[to]>mx){ mn=mx; mx=dp[to]; } else if(dp[to]>mn) mn=dp[to]; } dp[rt]=mx+1; ans=max(ans,mx+mn); } int main(){ int n; cin>>n; for(int i=1;i<=n-1;i++){ int u,v; cin>>u>>v; G[u].push_back(v); G[v].push_back(u); } dfs(1,-1); cout<<ans<<endl; return 0; }
-
树的重心:
#include <bits/stdc++.h> using namespace std; const int MAXN=1000010; const int inf=0x7f7f7f7f; int f[MAXN],size[MAXN],head[MAXN],dep[MAXN]; int n,center,sum; vector<int> G[MAXN]; queue<int> q; void dfs(int u,int fa){ size[u]=1; f[u]=0; for(int i=0;i<G[u].size();i++){ int v=G[u][i]; if(v==fa) continue; dfs(v,u); size[u]+=size[v]; f[u]=max(f[u],size[v]); } f[u]=max(f[u],n-size[u]); if(f[u]<f[center]||(f[u]==f[center]&&u<center)){ center=u; } } void bfs(){ q.push(center); while(!q.empty()){ int u=q.front(); q.pop(); for(int i=0;i<G[u].size();i++){ int v=G[u][i]; if(dep[v]||v==center) continue; dep[v]=dep[u]+1; sum+=dep[v]; q.push(v); } } } int main(){ cin>>n; for(int i=1;i<=n-1;i++){ int u,v; cin>>u>>v; G[u].push_back(v); G[v].push_back(u); } center=0; f[0]=inf; dfs(1,0); bfs(); cout<<center<<" "<<sum<<endl; return 0; }
-
树的中心:
#include<bits/stdc++.h> using namespace std; const int N = 10010, M = N * 2, INF = 0x3f3f3f3f; int n; int h[N], e[M], w[M], ne[M], idx; int d1[N], d2[N], p1[N], up[N]; bool is_leaf[N]; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ; } int dfs_d(int u, int father) { d1[u] = d2[u] = -INF; for (int i = h[u]; i != -1; i = ne[i]) { int j = e[i]; if (j == father) continue; int d = dfs_d(j, u) + w[i]; if (d >= d1[u]) { d2[u] = d1[u], d1[u] = d; p1[u] = j; } else if (d > d2[u]) d2[u] = d; } if (d1[u] == -INF) { d1[u] = d2[u] = 0; is_leaf[u] = true; } return d1[u]; } void dfs_u(int u, int father) { for (int i = h[u]; i != -1; i = ne[i]) { int j = e[i]; if (j == father) continue; if (p1[u] == j) up[j] = max(up[u], d2[u]) + w[i]; else up[j] = max(up[u], d1[u]) + w[i]; dfs_u(j, u); } } int main() { cin >> n; memset(h, -1, sizeof h); for (int i = 0; i < n - 1; i ++ ) { int a, b, c; cin >> a >> b >> c; add(a, b, c), add(b, a, c); } dfs_d(1, -1); dfs_u(1, -1); int res = d1[1]; for (int i = 2; i <= n; i ++ ) if (is_leaf[i]) res = min(res, up[i]); else res = min(res, max(d1[i], up[i])); printf("%d\n", res); return 0; }
-
LCA:
#include <bits/stdc++.h> #define ll long long #define ull unsigned long long using namespace std; typedef pair<int,int> PII; const int inf = 0x3f3f3f3f; const int mod = 998244353; const int N = 5e5+10; const int base = 233; int n,m,s; int pre[N][35],dep[N]; vector<int>v[N]; void init(int l,int ff){ pre[l][0] = ff; dep[l] = dep[ff] + 1; for(int r:v[l]){ if(r==ff)continue; init(r,l); } } void init(){ init(s,0); for(int k=1;k<=25;k++){ for(int i=1;i<=n;i++){ pre[i][k] = pre[ pre[i][k-1] ][k-1]; } } } int LCA(int x,int y){ //1.同深度 if(dep[x]<dep[y])swap(x,y); for(int k=25;k>=0;k--){ int fa = pre[x][k]; if(dep[fa]>=dep[y])x = fa; } if(x==y)return x; //2.一起向上跳 for(int k=25;k>=0;k--){ int fax = pre[x][k]; int fay = pre[y][k]; if(fax != fay){ x = fax; y = fay; } } return pre[y][0]; } inline void slove(){ scanf("%d%d%d",&n,&m,&s); for(int i=1;i<n;i++){ int x,y;scanf("%d%d",&x,&y); v[x].push_back(y); v[y].push_back(x); } init(); while (m--){ int x,y;scanf("%d%d",&x,&y); printf("%d\n",LCA(x,y)); } } int main(){ int TT=1; while(TT--) slove(); return 0; }
Tarjan 离线求 LCA
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <vector> using namespace std; typedef pair<int, int> PII; const int N = 10010, M = N * 2; int n, m; int h[N], e[M], w[M], ne[M], idx; int dist[N]; int p[N]; int res[M]; int st[N]; vector<PII> query[N]; // first存查询的另外一个点,second存查询编号 void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ; } void dfs(int u, int fa) { for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if (j == fa) continue; dist[j] = dist[u] + w[i]; dfs(j, u); } } int find(int x) { if (p[x] != x) p[x] = find(p[x]); return p[x]; } void tarjan(int u) { st[u] = 1; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if (!st[j]) { tarjan(j); p[j] = u; } } for (auto item : query[u]) { int y = item.first, id = item.second; if (st[y] ) { int anc = find(y); res[id] = anc; } } } int main() { scanf("%d%d", &n, &m); memset(h, -1, sizeof h); for (int i = 0; i < n - 1; i ++ ) { int a, b, c; scanf("%d%d%d", &a, &b, &c); add(a, b, c), add(b, a, c); } for (int i = 0; i < m; i ++ ) { int a, b; scanf("%d%d", &a, &b); if (a != b) { query[a].push_back({b, i}); query[b].push_back({a, i}); } } for (int i = 1; i <= n; i ++ ) p[i] = i; dfs(1, -1); tarjan(1); for (int i = 0; i < m; i ++ ) printf("%d\n", res[i]); return 0; }
-
树上差分
#include <bits/stdc++.h> #define ll long long #define ull unsigned long long using namespace std; typedef pair<int,int> PII; const int inf = 0x3f3f3f3f; const int mod = 998244353; const int N = 5e5+10; const int base = 233; int n,m,s; int pre[N][35],dep[N],val[N]; vector<int>v[N]; void init(int l,int ff){ dep[l] = dep[ff] + 1; pre[l][0] = ff; for(int r:v[l]){ if(r==ff)continue; init(r,l); } } void init(){ init(1,0); for(int k=1;k<=25;k++){ for(int i=1;i<=n;i++){ pre[i][k] = pre[pre[i][k-1]][k-1]; } } } int lca(int x,int y){ if(dep[x]<dep[y])swap(x,y); for(int k=25;k>=0;k--){ if(dep[pre[x][k]]>=dep[y]) x = pre[x][k]; } if(x==y)return x; for(int k=25;k>=0;k--){ if(pre[x][k] != pre[y][k])x = pre[x][k],y = pre[y][k]; } return pre[x][0]; } int ans = 0; void dfs(int l,int ff){ for(int r:v[l]){ if(r==ff)continue; dfs(r,l); val[l] += val[r]; } ans = max(ans,val[l]); } inline void slove(){ scanf("%d%d",&n,&m); for(int i=1;i<n;i++){ int x,y;scanf("%d%d",&x,&y); v[x].push_back(y); v[y].push_back(x); } init(); while (m--){ int x,y;scanf("%d%d",&x,&y); val[x]++; val[y]++; int LCA = lca(x,y); val[LCA]--; val[pre[LCA][0]]--; } dfs(1,0); printf("%d\n",ans); } int main(){ int TT=1; while(TT--)slove(); return 0; }
-
最长上升子序列:
-
\(O(n^2)\)
#include <bits/stdc++.h> using namespace std; int main(){ int n,a[100005],dp[100005],MAX; cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++){ MAX=0; for(int j=1;j<=i-1;j++) if(a[i]>a[j]) MAX=max(MAX,dp[j]); dp[i]=MAX+1; } MAX=0; for(int i=1;i<=n;i++) MAX=max(MAX,dp[i]); cout<<MAX<<endl; }
-
\(O(nlogn)\)
#include <bits/stdc++.h> using namespace std; int a[10005],f[10005]; int cnt; int main(){ int n; cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++){ int pos=lower_bound(f+1,f+cnt+1,a[i])-f; if(pos==cnt+1) f[++cnt]=a[i]; else f[pos]=a[i]; } cout<<cnt<<endl; for(int i=1;i<=cnt;i++) cout<<f[i]<<" "; return 0; }
-
-
最长公共子序列:
-
\(O(n^2)\)
#include<iostream> using namespace std; int dp[1001][1001],a1[2001],a2[2001],n,m; int main(){ cin>>n; for(int i=1;i<=n;i++)scanf("%d",&a1[i]); for(int i=1;i<=n;i++)scanf("%d",&a2[i]); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { dp[i][j]=max(dp[i-1][j],dp[i][j-1]); if(a1[i]==a2[j]) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1); } cout<<dp[n][n]; }
-
\(O(nlogn)\)
#include<bits/stdc++.h> using namespace std; int a[100001],b[100001],mp[100001],f[100001]; int main(){ int n; cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; mp[a[i]]=i; } for(int i=1;i<=n;i++){ cin>>b[i]; f[i]=0x7fffffff; } int len=0; f[0]=0; for(int i=1;i<=n;i++){ int l=0,r=len,mid; if(mp[b[i]]>f[len])f[++len]=mp[b[i]]; else { int pos=lower_bound(f,f+len,mp[b[i]])-f; f[pos]=min(mp[b[i]],f[pos]); } } cout<<len; return 0; }
-
-
01背包:
-
空间复杂度 \(O(nV)\)
#include <bits/stdc++.h> using namespace std; int f[1005][1005]; //f[i][j]表示选前i个物品重量不超过j的最大值 int main(){ int n,V; cin>>n>>V; for(int i=1;i<=n;i++){ int v,w; cin>>v>>w; for(int j=1;j<=V;j++){ if(v<=j) f[i][j]=max(f[i-1][j],f[i-1][j-v]+w); else f[i][j]=f[i-1][j]; } } cout<<f[n][V]<<endl; return 0; }
-
空间复杂度 \(O(V)\)
#include <bits/stdc++.h> using namespace std; int f[1005]; int main(){ int n,V; cin>>n>>V; for(int i=1;i<=n;i++){ int v,w; cin>>v>>w; for(int j=V;j>=v;j--) f[j]=max(f[j],f[j-v]+w); } cout<<f[V]<<endl; return 0; }
-
二维费用01背包
#include <iostream> using namespace std; const int N = 110; int n, V, M,f[N][N]; int main(){ cin >> n >> V >> M; for (int i = 0; i < n; i ++ ){ int v, m, w; cin >> v >> m >> w; for (int j = V; j >= v; j -- ) for (int k = M; k >= m; k -- ) f[j][k] = max(f[j][k], f[j - v][k - m] + w); } cout << f[V][M] << endl; return 0; }
-
01背包求具体方案:
#include <iostream> using namespace std; const int N = 1010; int n, m,v[N], w[N],f[N][N]; int main(){ cin >> n >> m; for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i]; for (int i = n; i >= 1; i -- ) //要求输出字典序最小所以考虑倒着做,即 f[1][m] 为最大价值 for (int j = 0; j <= m; j ++ ){ f[i][j] = f[i + 1][j]; if (j >= v[i]) f[i][j] = max(f[i][j], f[i + 1][j - v[i]] + w[i]); } int j = m; for (int i = 1; i <= n; i ++ ) if (j >= v[i] && f[i][j] == f[i + 1][j - v[i]] + w[i]){ cout << i << ' '; j -= v[i]; } return 0; }
-
-
完全背包:
-
空间复杂度 \(O(nV)\)
#include <bits/stdc++.h> using namespace std; int f[1005][1005]; int main(){ int n,V; cin>>n>>V; for(int i=1;i<=n;i++){ int v,w; cin>>v>>w; for(int j=0;j<=V;j++){ f[i][j]=f[i-1][j]; if(v<=j) f[i][j]=max(f[i][j],f[i][j-v]+w); } } cout<<f[n][V]<<endl; return 0; }
-
空间复杂度 \(O(V)\)
#include <bits/stdc++.h> using namespace std; int f[1005]; int main(){ int n,V; cin>>n>>V; for(int i=1;i<=n;i++){ int v,w; cin>>v>>w; for(int j=v;j<=V;j++) f[j]=max(f[j],f[j-v]+w); } cout<<f[V]<<endl; return 0; }
-
-
多重背包:
-
时间复杂度 \(O(nVs)\) (朴素版)
#include <bits/stdc++.h> using namespace std; const int N = 110; int v[N], w[N], s[N],f[N][N],n, m; int main(){ cin >> n >> m; for(int i = 1; i <= n; i ++) cin >> v[i] >> w[i] >> s[i]; for(int i = 1; i <= n; i ++){//枚举背包 for(int j = 1; j <= m; j ++){//枚举体积 for(int k = 0; k <= s[i]; k ++) if(j >= k * v[i]) f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]); } } cout << f[n][m] << endl; return 0; }
-
时间复杂度 \(O(n \ logs V)\) (二进制分解)
#include<bits/stdc++.h> using namespace std; const int N = 12010, M = 2010; int n, m,v[N], w[N],f[M]; int main(){ cin >> n >> m; int cnt = 0; //分组的组别 for(int i = 1;i <= n;i ++){ int a,b,s; cin >> a >> b >> s; int k = 1; // 组别里面的个数 while(k<=s){ cnt ++ ; //组别先增加 v[cnt] = a * k ; //整体体积 w[cnt] = b * k; // 整体价值 s -= k; // s要减小 k *= 2; // 组别里的个数增加 } //剩余的一组 if(s>0){ cnt ++ ; v[cnt] = a*s; w[cnt] = b*s; } } n = cnt ; //枚举次数正式由个数变成组别数 //01背包一维优化 for(int i = 1;i <= n ;i ++) for(int j = m ;j >= v[i];j --) f[j] = max(f[j],f[j-v[i]] + w[i]); cout << f[m] << endl; return 0; }
-
时间复杂度 \(O(nV)\) (单调队列优化)
#include<bits/stdc++.h> using namespace std; const int N = 20010; int n, m,f[N], g[N], q[N]; int main(){ cin >> n >> m; for (int i = 0; i < n; i ++ ){ int v, w, s; cin >> v >> w >> s; memcpy(g, f, sizeof f); //滚动数组 for (int j = 0; j < v; j ++ ){ int hh = 0, tt = -1; for (int k = j; k <= m; k += v){ //单调队列求区间最大值 if (hh <= tt && q[hh] < k - s * v) hh ++ ; while (hh <= tt && g[q[tt]] - (q[tt] - j) / v * w <= g[k] - (k - j) / v * w) tt -- ; q[ ++ tt] = k; f[k] = g[q[hh]] + (k - q[hh]) / v * w; } } } cout << f[m] << endl; return 0; }
-
-
分组背包:
-
\(O(nms)\)
#include<bits/stdc++.h> using namespace std; const int N=110; int f[N],v[N][N],w[N][N],s[N],n,m,k; int main(){ cin>>n>>m; for(int i=0;i<n;i++){ cin>>s[i]; for(int j=0;j<s[i];j++) cin>>v[i][j]>>w[i][j]; } for(int i=0;i<n;i++){ //枚举组 for(int j=m;j>=0;j--){ for(int k=0;k<s[i];k++){ //for(int k=s[i];k>=0;k--)也可以 if(j>=v[i][k]) f[j]=max(f[j],f[j-v[i][k]]+w[i][k]); } } } cout<<f[m]<<endl; }
-
分组背包求具体方案
#include<bits/stdc++.h> using namespace std; const int N=1010; int f[N][N],g[N][N],res[N]; //g[i][j] 为第i组的体积为j的价值 int main(){ int n,m; cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>g[i][j]; for(int i=n;i;i--) //要求输出字典序最小所以考虑倒着做,即 f[1][m] 为最大价值 for(int j=0;j<=m;j++) for(int k=0;k<=j;k++) f[i][j]=max(f[i][j],f[i+1][j-k]+g[i][k]); cout<<f[1][m]<<endl; int j=m; for(int i=1;i<=n;i++) for(int k=0;k<=j;k++) if(f[i][j]==f[i+1][j-k]+g[i][k]){ res[i]=k; j-=k; break; } for(int i=1;i<=n;i++) cout<<i<<" "<<res[i]<<endl; return 0; }
-
-
其他dp
-
-
2.2.5 数学与其他
-
康托展开:
#include<cstdio> using namespace std; #define N 1000001 int n,tr[N]; long long ans,fac[N]; void add(int x,int k) { for (; x<=n; x+=x&-x) tr[x]+=k; } int query(int x) { int t=0; for (; x; x-=x&-x) t+=tr[x]; return t; } int main() { scanf("%d",&n); fac[0]=1; for (int i=1; i<=n; i++) { fac[i]=fac[i-1]*i%998244353; add(i,1); } for (int i=1,x; i<=n; i++) { scanf("%d",&x); ans=(ans+(query(x)-1)*fac[n-i])%998244353; add(x,-1); } printf("%lld",ans+1); return 0; }
-
中国剩余定理:
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N = 10; int n; int A[N], B[N]; LL exgcd(LL a, LL b, LL &x, LL &y) // 扩展欧几里得算法, 求x, y,使得ax + by = gcd(a, b){ if (!b){ x = 1; y = 0; return a; } LL d = exgcd(b, a % b, y, x); y -= (a / b) * x; return d; } int main(){ scanf("%d", &n); LL M = 1; for (int i = 0; i < n; i ++ ){ scanf("%d%d", &A[i], &B[i]); M *= A[i]; } LL res = 0; for (int i = 0; i < n; i ++ ) { LL Mi = M / A[i]; LL ti, x; exgcd(Mi, A[i], ti, x); res = (res + (__int128)B[i] * Mi * ti) % M; // B[i] * Mi * ti可能会超出long long范围,所以需要转化成__int128 } cout << (res % M + M) % M << endl; return 0; }
-
线性筛:
void work(int n){ numlist[1]=1; for(int i=2;i<=n;i++){ if(numlist[i]==false) prime[++cnt]=i; for(int j=1; j<=cnt&&i*prime[j]<=n; j++){ numlist[i*prime[j]] = true ; if(i%prime[j]==0) break; } } }
-
欧拉筛
int st[N]; // 初始化为0, 0表示质数,1表示合数 for(int i = 2; i <= n; i++){ for(int j = 2; j <= i / j; j++){ if(i % j == 0){ st[i] = 1; } } }
-
快速幂:
int qmi(int a, int k){ int res = 1; while (k){ if (k & 1) res = (LL)res * a % mod; a = a * a % mod; k >>= 1; } return res; }
-
欧拉函数:
void init(int n) { phi[1] = 1; for (int i = 2; i <= n; i ++ ) { if (!st[i]) { primes[cnt ++ ] = i; phi[i] = i - 1; } for (int j = 0; primes[j] * i <= n; j ++ ) { st[i * primes[j]] = true; if (i % primes[j] == 0) { phi[i * primes[j]] = phi[i] * primes[j]; break; } phi[i * primes[j]] = phi[i] * (primes[j] - 1); } } }
-
拓展欧几里得:
int exgcd(int a, int b, int &x, int &y) { if (!b) { x = 1, y = 0; return a; } int d = exgcd(b, a % b, y, x); y -= a / b * x; return d; }
-
逆元
-
费马小定理求逆元:
LL pow_mod(LL a, LL b, LL p){//a的b次方求余p LL ret = 1; while(b){ if(b & 1) ret = (ret * a) % p; a = (a * a) % p; b >>= 1; } return ret; } LL Fermat(LL a, LL p){//费马求a关于b的逆元 return pow_mod(a, p-2, p); }
-
扩展欧几里德求逆元:
#include<cstdio> typedef long long LL; void ex_gcd(LL a, LL b, LL &x, LL &y, LL &d){ if (!b) {d = a, x = 1, y = 0;} else{ ex_gcd(b, a % b, y, x, d); y -= x * (a / b); } } LL inv(LL t, LL p){//如果不存在,返回-1 LL d, x, y; ex_gcd(t, p, x, y, d); return d == 1 ? (x % p + p) % p : -1; } int main(){ LL a, p; while(~scanf("%lld%lld", &a, &p)){ printf("%lld\n", inv(a, p)); } }
-
欧拉定理求逆元:
#include <bits/stdc++.h> using namespace std; const int N = 1e6 + 10; vector<int> prime; int phi[N]; bool st[N]; void getphi(int n) { memset(st, 1, sizeof st); for (int i = 2; i <= n; i++) { //遍历区间内所有值 if (st[i]) prime.push_back(i), phi[i] = i - 1; //性质1 for (int j = 0; j < prime.size() && prime[j] <= n / i; j++) { int p = prime[j]; st[p * i] = 0; if (i % p == 0) { //i 和 p的位置不要搞反 phi[p * i] = phi[i] * p; //性质3 break; } phi[i * p] = phi[i] * (p - 1); //性质2+性质1 } } } int main() { int n; getphi(N); while (cin >> n, n) { cout << phi[n] << endl; } return 0; }
-
线性求逆元:
Inv[ 1 ] = 1; for( int i = 2; i <= n; i++ ) Inv[ i ] = ( p - p / i ) * Inv[ p % i ] % p;
-
线性求阶乘逆元:
int inv( int b, int p ) { int a, k; exPower( b, p, a, k ); if( a < 0 ) a += p; return a; } void init( int n ) { Fact[ 0 ] = 1; for( int i = 1; i <= n; ++i ) Fact[ i ] = Fact[ i - 1 ] * i % Mod; INV[ n ] = inv( Fact[ n ], Mod ); for( int i = n - 1; i >= 0; --i ) INV[ i ] = INV[ i + 1 ] * ( i + 1 ) % Mod; return; }
-
-
组合数:
-
杨辉三角:
#include <iostream> using namespace std; typedef long long LL; const int N = 2010, MOD = 1e9+7; int n; int c[N][N]; void init() { for (int i = 0; i < N; ++i) for (int j = 0; j <= i; ++j) if (!j) c[i][j] = 1; else c[i][j] = (c[i-1][j] + c[i-1][j-1]) % MOD; } int main() { init(); int n; cin >> n; while (n --) { int a, b; cin >> a >> b; cout << c[a][b] << endl; } return 0; }
-
预处理阶乘+逆元:
#include <iostream> #include <algorithm> using namespace std; typedef long long LL; const int N = 1e5+5, MOD = 1e9+7; int fact[N], infact[N]; int qmi(int a, int k, int p) { LL res = 1; while (k) { if (k & 1) res = (LL)res * a % p; k >>= 1; a = (LL)a * a % p; } return res; } int main() { fact[0] = infact[0] = 1; for (int i = 1 ; i < N; ++i) { fact[i] = (LL)fact[i - 1] * i % MOD; infact[i] = (LL)infact[i - 1] * qmi(i, MOD - 2, MOD) % MOD; } int n; cin >> n; while (n --) { int a, b; cin >> a >> b; cout << (LL)fact[a] * infact[a - b] % MOD * infact[b] % MOD << endl; } return 0; }
-
Lucas定理
#include <iostream> using namespace std; typedef long long LL; int qmi(int a, int k, int p) { int res = 1 % p; while (k) { if (k & 1) res = (LL)res * a % p; a = (LL)a * a % p; k >>= 1; } return res; } // 定义求解 int C(int a, int b, int p) { if (b > a) return 0; int res = 1; for (int i = 1, j = a; i <= b; ++i, --j) { res = (LL)res * j % p; res = (LL)res * qmi(i, p - 2, p) % p; } return res; } int lucas(LL a, LL b, LL p) { // 注意LL参数类型 if (a < p && b < p) return C(a, b, p); return (LL)C(a % p, b % p, p) * lucas(a / p, b / p, p) % p; // 递归让其到p范围内求解 } int main() { int n; cin >> n; while (n --) { LL a, b, p; cin >> a >> b >> p; cout << (LL)lucas(a, b, p) << endl; } return 0; }
-
-
高斯消元:
#include <iostream> #include <cmath> using namespace std; const int N = 105; const double eps = 1e-6; int n; double a[N][N]; int gauss() { int c, r; for (c = 0, r = 0; c < n; ++c) { // c列r行,遍历列 int t = r; for (int i = r; i < n; ++i) // 寻找列主元,拿t记录 if (fabs(a[i][c]) > fabs(a[t][c])) t = i; if (fabs(a[t][c]) < eps) continue; // 如果列主元为0,不必考虑,当前列全为0 // 交换列主元t行与当前r行的数 for (int i = c; i < n + 1; ++i) swap(a[t][i], a[r][i]); // 当前列主元已经被交换到了r行,需要从后向前进行处理,避免主对角线元素变成1 for (int i = n; i >= c; --i) a[r][i] /= a[r][c]; // 列主消元 for (int i = r + 1; i < n; ++i) if (fabs(a[i][c]) > eps) for (int j = n; j >= c; --j) a[i][j] -= a[r][j] * a[i][c]; ++r; } if (r < n) { for (int i = r; i < n; ++i) if (fabs(a[i][n]) > eps) return 2; // 0x=1 则无解 return 1; // 0x=0 无穷多解 } // 上三角阶梯型矩阵 // 直接求解即可,最后一列放置结果 for (int i = n - 1; i >= 0; --i) for (int j = i + 1; j < n; ++j) a[i][n] -= a[j][n] * a[i][j]; return 0; } int main() { cin >> n; for (int i = 0; i < n; ++i) for (int j = 0; j < n + 1; ++j) cin >> a[i][j]; int t = gauss(); if (t == 0) { for (int i = 0; i < n; ++i) printf("%.2lf\n", a[i][n]); } else if (t == 1) puts("Infinite group solutions"); else puts("No solution"); return 0; }
-
龟速乘
LL qmul(LL a, LL k, LL p) { LL res = 0; while (k){ if (k & 1) res = (res + a) % p; a = (a + a) % p; k >>= 1; } return res; }
-
莫比乌斯函数
mu[1]=1; for(i=2;i<=n;i++){ if(!not_prime[i]){ prime[++tot]=i; mu[i]=-1; } for(j=1;prime[j]*i<=n;j++){ not_prime[prime[j]*i]=1; if(i%prime[j]==0){ mu[prime[j]*i]=0; break; } mu[prime[j]*i]=-mu[i]; } }
-
============================================
失配树
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << '=' << x << endl
inline int rd(){
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9'){
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
void print(int x){
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
print(x / 10);
putchar(x % 10 + '0');
return;
}
namespace Star_F{
const int kmaxlog = 25;
const int kmaxn = 1000000 + 5;
const int ml = 21;
int fail[kmaxn];
int fa[kmaxn][kmaxlog];
int dis[kmaxn];
char s[kmaxn];
int n, q;
void init() {
int p = 0;
FOR(i, 2, n) {
while (p && s[p + 1] != s[i])
p = fail[p];
if (s[p + 1] == s[i]) ++p;
fail[i] = p;
dis[i] = dis[p] + 1;
fa[i][0] = p;
}
FOR(k, 1, ml) {
FOR(i, 1, n)
fa[i][k] = fa[fa[i][k - 1]][k - 1];
}
}
int jmp(int x, int h) {
if (dis[x] <= h) return x;
ROF(i, ml, 0) {
if (dis[fa[x][i]] > h)
x = fa[x][i];
}
return fa[x][0];
}
int lca(int x, int y) {
if (dis[x] < dis[y]) swap(x, y);
x = jmp(x, dis[y]);
ROF(i, ml, 0) {
if (fa[x][i] != fa[y][i]) {
x = fa[x][i];
y = fa[y][i];
}
}
return fa[x][0];
}
void Main() {
scanf("%s%d", &s[1], &q);
n = strlen(s + 1);
init();
int a, b;
while (q--) {
scanf("%d%d", &a, &b);
printf("%d\n", lca(a, b));
}
}
}
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
return Star_F::Main(), 0;
}
无旋Ttreap
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define MAX 100100
inline int read() {
int x = 0;
bool neg = false;
char ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') {
neg = true;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch - '0');
ch = getchar();
}
return neg ? -x : x;
}
void print(int x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
namespace Star_F {
const int N = 1e6 + 1e5 + 1;
unsigned long long seed = 1;
std::mt19937 rnd(std::random_device{}());
struct fhq {
struct {
int l, r; // 左右孩子
int val; // BST 的权值
int rnd; // 堆的随机值
int size; // 树的大小
} tr[N];
int root = 0, n = 0; // 根节点, 最新节点的下标
inline int _rand() { return rnd(); }
// 创建一个新节点
int newnode(int v) {
tr[++n].val = v;
tr[n].rnd = _rand();
tr[n].size = 1;
return n;
}
// 更新树的大小
void pushup(int p) { tr[p].size = tr[tr[p].l].size + tr[tr[p].r].size + 1; }
// 按值 v 进行分裂, 左子树 <=v, 右子树 > v
// x, y 是分裂后的两颗子树的根, 执行完成后 x.val <= v < y.val
void split(int p, int v, int &x, int &y) {
if (!p) { // 空树
x = y = 0;
return;
}
if (tr[p].val <= v) {
// 递归分裂 p 的右子树
// 左子树的根 x 已确定, y 需要继续向下带
// 同时, p 的右节点需要指向下一层指向的左子树的根 x
x = p;
split(tr[p].r, v, tr[p].r, y);
} else { // 递归分裂 p 的左子树
y = p;
split(tr[p].l, v, x, tr[p].l);
}
// 最后, 向上更新每个子树的大小
pushup(p);
}
// 按堆的随机值合并两颗子树, 返回合并后的根
// 要求 x 树所有节点的 val 要 <= y 树所有节点的 val 值
int merge(int x, int y) {
if (!x || !y) return x + y; // 存在空树的情况
if (tr[x].rnd < tr[y].rnd) {
// 应把 x 放上面, 先递归合并 x 的右子树 r 和 y 得到新树 z
// 因为 x 权值更小, 所以把 z 作为 x 的右孩子
tr[x].r = merge(tr[x].r, y);
pushup(x);
return x;
} else {
tr[y].l = merge(x, tr[y].l);
pushup(y);
return y;
}
}
explicit fhq() { memset(tr, 0, sizeof tr); }
int size() { return tr[root].size; }
// 插入一个值
void insert(int v) {
// 按 v 分裂, 找到插入点 x <=v < y
int x, y;
split(root, v, x, y);
// 新建节点
int z = newnode(v);
// 依次合并 x, z 和 y (权值 val 也满足如此顺序)
root = merge(merge(x, z), y);
}
// 删除一个值
void del(int v) {
int x, y, z;
// 先找到 v 的分割点 => x <= v < z
split(root, v, x, z);
// 再按 v-1 分裂 x 树 => x <= v-1 < y <= v
split(x, v - 1, x, y);
// y 就是值等于 v 的节点, 删除它
// 如果找不到 v, y 就是空树, 其左右孩子都是 0, 不影响
y = merge(tr[y].l, tr[y].r);
// 再把 x, y, z 合并起来
root = merge(merge(x, y), z);
}
// 查找排名, 满足 < v 的个数+1
int rank(int v) {
int x, y;
split(root, v - 1, x, y);
int ans = tr[x].size + 1;
root = merge(x, y);
return ans;
}
// 从子树 p 找第 k 小
int topk(int p, int k) {
int lsz = tr[tr[p].l].size;
if (k == lsz + 1) return tr[p].val;
if (k <= lsz) return topk(tr[p].l, k);
return topk(tr[p].r, k - lsz - 1);
}
// 前驱, 严格 <v 的值
int get_pre(int v) {
int x, y;
split(root, v - 1, x, y);
int ans = topk(x, tr[x].size);
root = merge(x, y);
return ans;
}
// 后继, 严格 > v 的值
int get_suc(int v) {
int x, y;
split(root, v, x, y);
int ans = topk(y, 1);
root = merge(x, y);
return ans;
}
};
void Main() {
int n = read();
int m = read();
// 初始化
fhq t;
int val;
for (int i = 0; i < n; i++) {
val = read();
t.insert(val);
}
// 操作
int last = 0, ans = 0;
while (m--) {
int opt = read();
int x = read();
x ^= last;
switch (opt) {
case 1:
t.insert(x);
break;
case 2:
t.del(x);
break;
case 3:
last = t.rank(x);
ans ^= last;
break;
case 4:
last = t.topk(t.root, x);
ans ^= last;
break;
case 5:
last = t.get_pre(x);
ans ^= last;
break;
case 6:
last = t.get_suc(x);
ans ^= last;
break;
}
}
print(ans);
putchar('\n');
}
}
signed main() {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
return Star_F::Main(), 0;
}
P2042 [NOI2005] 维护数列 (含区间操作的无旋Treap)
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 500005;
unsigned long long seed = 1;
// 要插入的数字
const int MAX_INSERTS = 4000005;
int to_inserts[MAX_INSERTS];
#define ls tr[p].l
#define rs tr[p].r
struct {
int l, r; // 左右孩子
int val; // 节点权值
int rnd; // 随机值
int size; // 子树大小
int rev_tag; // 翻转延迟标记
int cov_tag; // 覆盖延迟标记
int cov_val; // 覆盖更新的值
int sum; // 子树的区间和
int mpre; // 区间最大前缀和
int msuf; // 区间最大后缀和
int msum; // 子树内最大的区间和
} tr[N];
int root = 0, tt = 0;
// 垃圾回收栈
int gc_stack[N];
int gc_top = 0;
inline int _rand() {
seed *= 333333;
return int(seed);
}
int newnode(int val) {
int i = gc_top ? gc_stack[--gc_top] : ++tt;
memset(&tr[i], 0, sizeof tr[i]);
tr[i].rnd = _rand();
tr[i].size = 1;
tr[i].msum = tr[i].sum = tr[i].val = val;
tr[i].mpre = tr[i].msuf = max(0, val);
return i;
}
void gc(int p) {
if (!p) return;
gc_stack[gc_top++] = p;
gc(ls), gc(rs);
}
void pushup(int p) {
tr[p].size = tr[ls].size + tr[rs].size + 1;
tr[p].sum = tr[ls].sum + tr[rs].sum + tr[p].val;
// 父区间的前缀和要么是左子区间的, 要么是 l.sum + p + r.mpre
tr[p].mpre = max({tr[ls].mpre, tr[ls].sum + tr[p].val + tr[rs].mpre, 0});
// 同理
tr[p].msuf = max({tr[rs].msuf, tr[rs].sum + tr[p].val + tr[ls].msuf, 0});
// 路过 p 的情况
tr[p].msum = max(tr[ls].msuf + tr[p].val + tr[rs].mpre, tr[p].val);
// 不路过 p 的情况,取最大
if (ls) tr[p].msum = max(tr[p].msum, tr[ls].msum);
if (rs) tr[p].msum = max(tr[p].msum, tr[rs].msum);
}
// 执行 p 区间的覆盖
void do_cover(int p) {
if (!tr[p].cov_tag) return;
// 修改值
int c = tr[p].val = tr[p].cov_val;
// 因为采用延迟更新
// 所以子树的和不再等于: 左右子树的和+p.val
// 这里要同步修改掉
int s = tr[p].sum = c * tr[p].size;
tr[p].mpre = tr[p].msuf = max(0, s);
tr[p].msum = max(c, s);
// 下传标记
tr[ls].cov_tag = 1, tr[ls].cov_val = c;
tr[rs].cov_tag = 1, tr[rs].cov_val = c;
// 清理当前标记
tr[p].cov_tag = 0;
tr[p].cov_val = 0;
}
void do_reverse(int p) {
if (!tr[p].rev_tag) return;
// 翻转
swap(ls, rs);
// 还要翻转前后缀和
swap(tr[p].mpre, tr[p].msuf);
// 向下标记
tr[ls].rev_tag ^= 1;
tr[rs].rev_tag ^= 1;
// 清理当前标记
tr[p].rev_tag = 0;
}
void pushdown(int p) {
if (!p) return;
// 区间翻转
do_reverse(p);
// 向下标记, 多执行一层
if (ls) do_reverse(ls);
if (rs) do_reverse(rs);
// 区间覆盖
do_cover(p);
// 区间覆盖要多向前执行一层
// 因为左右子树其中一个变化, 另一半也要变化
// 这样 sum 查询才是准的
if (ls) do_cover(ls);
if (rs) do_cover(rs);
}
// 按位置分裂,左边的树位置 [1,k]
void split(int p, int k, int &x, int &y) {
if (!p) {
x = y = 0;
return;
}
pushdown(p);
if (tr[ls].size < k) {
x = p;
k -= tr[ls].size + 1;
split(rs, k, rs, y);
} else {
y = p;
split(ls, k, x, ls);
}
pushup(p);
}
int merge(int x, int y) {
if (!x || !y) return x + y;
if (tr[x].rnd < tr[y].rnd) {
pushdown(x);
tr[x].r = merge(tr[x].r, y);
pushup(x);
return x;
} else {
pushdown(y);
tr[y].l = merge(x, tr[y].l);
pushup(y);
return y;
}
}
// 追加一个新数字
void append(int val) { root = merge(root, newnode(val)); }
// 对 to_inserts 二分建树
int build(int l, int r) {
if (l == r) return newnode(to_inserts[l]);
int mid = (l + r) >> 1;
return merge(build(l, mid), build(mid + 1, r));
}
// 在 k 处插入 tot 个数字
// k = 0 时在队头插入
void insert(int k, int tot) {
int x, y;
split(root, k, x, y);
int z = build(1, tot);
root = merge(merge(x, z), y);
}
// 朴素做法,TEL
void insert1(int k, int tot) {
int x, y;
split(root, k, x, y);
for (int i = 1; i <= tot; i++) x = merge(x, newnode(to_inserts[i]));
root = merge(x, y);
}
// 从第 k 个开始, 切出子树 x, y, z
void help_split(int &x, int &y, int &z, int k, int tot) {
int l = k, r = k + tot - 1;
split(root, r, x, z);
split(x, l - 1, x, y);
}
// 在 k 处删除 tot 个数字
void del(int k, int tot) {
int x, y, z;
help_split(x, y, z, k, tot);
gc(y);
root = merge(x, z);
}
// 在 k 处翻转 tot 个
void reverse(int k, int tot) {
int x, y, z;
help_split(x, y, z, k, tot);
// 打标
tr[y].rev_tag ^= 1;
// 执行一层
do_reverse(y);
root = merge(merge(x, y), z);
}
// 从当前数列的第 k 个数字开始的连续 tot 个数字统一修改为 c
void makesame(int k, int tot, int c) {
int x, y, z;
help_split(x, y, z, k, tot);
// 打标
tr[y].cov_tag = 1;
tr[y].cov_val = c;
// 执行一层
do_cover(y);
root = merge(merge(x, y), z);
}
// 计算从当前数列的第 k 个数字开始的 tot 个数字的和并输出
int getsum(int k, int tot) {
int x, y, z;
help_split(x, y, z, k, tot);
int ans = tr[y].sum;
root = merge(merge(x, y), z);
return ans;
}
// 求出当前数列中和最大的一段子列,并输出最大和
int maxsum() { return tr[root].msum; }
void output(int p) {
if (!p) return;
pushdown(p);
output(tr[p].l);
printf("%d ", tr[p].val);
output(tr[p].r);
}
int main(void) {
int n, m;
scanf("%d%d", &n, &m);
// 初始化
for (int i = 1; i <= n; i++) {
int val;
scanf("%d", &val);
append(val);
}
// 读取操作
char op[10];
while (m--) {
scanf("%9s", op);
int k, tot, c, ans;
switch (op[0]) {
case 'I': // 插入
scanf("%d%d", &k, &tot);
for (int i = 1; i <= tot; i++) scanf("%d", &to_inserts[i]);
insert(k, tot);
break;
case 'D': // 删除
scanf("%d%d", &k, &tot);
del(k, tot);
break;
case 'R': // 翻转
scanf("%d%d", &k, &tot);
reverse(k, tot);
break;
case 'G': // 区间求和
scanf("%d%d", &k, &tot);
ans = getsum(k, tot);
printf("%d\n", ans);
break;
case 'M':
if (op[2] == 'K') { // 覆盖
scanf("%d%d%d", &k, &tot, &c);
makesame(k, tot, c);
} else { // 求最大子列和
ans = maxsum();
printf("%d\n", ans);
}
break;
}
}
return 0;
}
P5905 【模板】全源最短路(Johnson)
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << '=' << x << endl
inline int rd() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
void print(ll x) {
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
print(x / 10);
putchar(x % 10 + '0');
return;
}
namespace Star_F {
struct edge {
int v, w, next;
} e[10005];
struct node {
int dis, id;
bool operator<(const node& a) const { return dis > a.dis; }
node(int d, int x) { dis = d, id = x; }
};
const int INF = 1e9;
int head[5005], vis[5005], t[5005];
int cnt, n, m;
long long h[5005], dis[5005];
void addedge(int u, int v, int w) {
e[++cnt].v = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt;
}
bool spfa(int s) {
queue<int> q;
memset(h, 63, sizeof(h));
h[s] = 0, vis[s] = 1;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
vis[u] = 0;
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].v;
if (h[v] > h[u] + e[i].w) {
h[v] = h[u] + e[i].w;
if (!vis[v]) {
vis[v] = 1;
q.push(v);
t[v]++;
if (t[v] == n + 1) return false;
}
}
}
}
return true;
}
void dijkstra(int s) {
priority_queue<node> q;
for (int i = 1; i <= n; i++) dis[i] = INF;
memset(vis, 0, sizeof(vis));
dis[s] = 0;
q.push(node(0, s));
while (!q.empty()) {
int u = q.top().id;
q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].v;
if (dis[v] > dis[u] + e[i].w) {
dis[v] = dis[u] + e[i].w;
if (!vis[v]) q.push(node(dis[v], v));
}
}
}
return;
}
void Main() {
n = rd(); m = rd();
for (int i = 1; i <= m; i++) {
int u = rd(), v = rd(), w = rd();
addedge(u, v, w);
}
for (int i = 1; i <= n; i++) addedge(0, i, 0);
if (!spfa(0)) {
cout << -1 << endl;
return;
}
for (int u = 1; u <= n; u++)
for (int i = head[u]; i; i = e[i].next) e[i].w += h[u] - h[e[i].v];
for (int i = 1; i <= n; i++) {
dijkstra(i);
long long ans = 0;
for (int j = 1; j <= n; j++) {
if (dis[j] == INF)
ans += j * INF;
else
ans += j * (dis[j] + h[j] - h[i]);
}
cout << ans << endl;
}
}
}
signed main() {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
return Star_F::Main(), 0;
}
整除分块
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
for (int l = 1, r; l <= n;l=r+1){
r = n / (n / l);
cout << l << "~" << r << ":" << n/l << endl;
}
return 0;
}
P3846 [TJOI2007] 可爱的质数/【模板】BSGS
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << '=' << x << endl
#define int ll
#define HASH_MOD 76799777LL
inline ll rd() {
ll x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
void print(ll x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
namespace Star_F {
map<int, int> hash;
ll qpow(ll A, ll B, ll C) {
if (B == 0) return 1;
if (B == 1) return A;
ll t = qpow(A, B >> 1, C);
t = t * t % C;
if (B & 1) t = t * A % C;
return t;
}
ll BSGS(ll A, ll B, ll C) {
const int sizes = ceil(sqrt(C));
ll base = B % C;
hash[base] = 0;
for (int i = 1; i <= sizes; i++) {
base = base * A % C;
hash[base] = i;
}
base = qpow(A, sizes, C);
ll tmp = 1;
for (ll i = 1; i <= sizes; i++) {
tmp = tmp * base % C;
if (hash[tmp])
return ((i * sizes - hash[tmp]) % C + C) % C;
}
return -1;
}
ll P, B, N;
void Main() {
P = rd();
B = rd();
N = rd();
if (!(B % P)) {
printf("no solution\n");
return;
}
ll ans = BSGS(B, N, P);
if (ans != -1) printf("%lld", ans);
else printf("no solution");
}
}
signed main() {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
return Star_F::Main(), 0;
}
补充:
树状数组求逆序对
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 500005;
int m;
int a[N], b[N], c[N];
int lowbit(int x){
return x & (-x);
}
void add(int x, int y)
{
for (int i = x; i <= m; i+=lowbit(i))
c[i] += y;
}
int sum(int x)
{
int res = 0;
for (int i = x; i; i -= lowbit(i))
res += c[i];
return res;
}
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i], b[i] = a[i];
sort(b + 1, b + n + 1);
m = unique(b + 1, b + n + 1) - b - 1;
long long ans = 0;
for (int i = n; i; i--)
{
int k = lower_bound(b + 1, b + m + 1, a[i]) - b;
ans += sum(k - 1);
add(k, 1);
}
cout << ans << endl;
return 0;
}
树的重链剖分:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 100005;
int n, m, r, p, vistie;
int a[N], fa[N], wc[N], sz[N], top[N], dfn[N], rdfn[N], dep[N];
vector<int> e[N];
void dfs1(int u,int f){
fa[u] = f;
sz[u] = 1;
dep[u] = dep[f] + 1;
for (int i = 0; i < e[u].size();i++){
if(e[u][i]!=f){
int v = e[u][i];
dfs1(v, u);
sz[u] += sz[v];
if(sz[v]>sz[wc[u]])
wc[u] = v;
}
}
}
void dfs2(int u,int Top){
dfn[u] = ++vistie;
rdfn[vistie] = u;
top[u] = Top;
if(wc[u]!=0){
dfs2(wc[u], Top);
for (int i = 0; i < e[u].size();i++){
if(e[u][i]!=fa[u]&&e[u][i]!=wc[u]){
dfs2(e[u][i], e[u][i]);
}
}
}
}
ll w[N * 4];
void pushup(int u){
w[u] = (w[u << 1] + w[u << 1 | 1]) % p;
}
void build(int u,int L,int R){
if(L==R){
w[u] = a[rdfn[L]];
return;
}
int mid = L + R >> 1;
build(u << 1, L, mid);
build(u << 1 | 1, mid + 1, R);
pushup(u);
}
bool InRange(int L,int R,int l,int r){
return (l <= L) && (R <= r);
}
bool OutoRange(int L,int R,int l,int r){
return (L > r) || (R < l);
}
ll lzy[N * 4];
void maketag(int u,int len,int x){
lzy[u] = (lzy[u] + x) % p;
w[u] = (w[u] + len * x) % p;
}
void pushdown(int u,int L,int R){
int mid = L + R >> 1;
maketag(u << 1, mid - L + 1, lzy[u]);
maketag(u << 1 | 1, R - mid, lzy[u]);
lzy[u] = 0;
}
ll query(int u,int L,int R,int l,int r){
if(InRange(L,R,l,r))
return w[u];
else if(!OutoRange(L,R,l,r)){
int mid = L + R >> 1;
pushdown(u, L, R);
return (query(u << 1, L, mid, l, r) + query(u << 1 | 1, mid + 1, R, l, r) )% p;
}
else
return 0;
}
void update(int u,int L,int R,int l,int r,ll x){
if(InRange(L,R,l,r))
maketag(u, R - L + 1, x);
else if(!OutoRange(L,R,l,r)){
int mid = L + R >> 1;
pushdown(u, L, R);
update(u << 1, L, mid, l, r, x);
update(u << 1 | 1, mid + 1, R, l, r, x);
pushup(u);
}
}
void upd(int x,int y,int z){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])
swap(x, y);
update(1, 1, n, dfn[top[x]], dfn[x], z);
x = fa[top[x]];
}
update(1, 1, n, min(dfn[x], dfn[y]), max(dfn[x], dfn[y]), z);
}
int qry(int x,int y){
ll ans = 0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])
swap(x, y);
ans += query(1, 1, n, dfn[top[x]], dfn[x]);
ans %= p;
x = fa[top[x]];
}
return (ans + query(1, 1, n, min(dfn[x], dfn[y]), max(dfn[x], dfn[y]))) % p;
}
int main(){
cin >> n >> m >> r >> p;
for (int i = 1;i<=n;i++)
cin >> a[i];
for (int i = 1; i < n;i++){
int u, v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(r, 0);
dfs2(r, 0);
build(1, 1, n);
while(m--){
int o, x, y, z;
cin >> o;
if(o==1){
cin >> x >> y >> z;
upd(x, y, z);
}
else if(o==2){
cin >> x >> y;
cout << qry(x, y) << endl;
}
else if(o==3){
cin >> x >> y;
update(1, 1, n, dfn[x], dfn[x] + sz[x] - 1, y);
}
else{
cin >> x;
cout << query(1, 1, n, dfn[x], dfn[x] + sz[x] - 1) % p << endl;
}
}
return 0;
}