inlineintrd(){ int k = 0, w = 1; char c = getchar(); while (c < '0' || c > '9') { if (ch == '-') w = -1; c = getchar(); } while (c >= '0' && c <= '9') { k = k * 10 + (c ^ '0'), c = getchar(); } return k; }
// 题目与边本身有关,边上记录属性,比如求最小路,边排序 structEdge{ int u,v; longlong w; };
// 链式前向星,题目与边无关,只是方向 to, 同起点的下一条边 next // 数组下标表示出点,也就是上面的 u structEdge { int to, next; longlong w;} E[N<<1]; int head[N], ecnt=0; fill(head, head + n + 1, 0); inlinevoidadd_edge(int u, int v, longlong w){ E[++ecnt] = {v, head[u], w}; head[u] = ecnt; } for (int i=head[u]; i; i=E[i].next)
structSparseTable { int n, K; vector<int> lg; vector<vector<int>> st; SparseTable(const vector<int>& a) { n = a.size(); lg.resize(n+1); lg[1] = 0; for (int i = 2; i <= n; i++) lg[i] = lg[i/2] + 1; K = lg[n] + 1;
#include<bits/stdc++.h> usingnamespace std; int a[32][32],b[32][32]; int dx[5]={0,-1,1,0,0}; int dy[5]={0,0,0,-1,1};//第一个表示不动,是充数的,后面的四个分别是上下左右四个方向 int n,i,j; voiddfs(int p,int q){ int i; if (p<0||p>n+1||q<0||q>n+1||a[p][q]!=0) return; //如果搜过头或者已经被搜过了或者本来就是墙的就往回 a[p][q]=1;//染色 for (i=1;i<=4;i++) dfs(p+dx[i],q+dy[i]);//向四个方向搜索 } intmain(){ cin>>n; for (i=1;i<=n;i++) for (j=1;j<=n;j++){ cin>>b[i][j];//其实不拿两个数组也可以,不过我喜欢啦 if (b[i][j]==0) a[i][j]=0; else a[i][j]=2; } dfs(0,0);//搜索 从 0,0 开始搜 for (i=1;i<=n;i++){ for (j=1;j<=n;j++) if (a[i][j]==0) cout<<2<<' '; //如果染过色以后 i,j 那个地方还是 0,说明没有搜到,就是周围有墙,当然就是被围住了,然后输出 2 else cout<<b[i][j]<<' '; //因为被染色了,本来没有被围住的水和墙都染成了 1,所以就输出 b[i][j] cout<<'\n';//换行 } }
int dx[4] = {1, -1, 0, 0}; int dy[4] = {0, 0, 1, -1};
voiddfs(int i, int j, int n, int m, vector<string>& ta){ if (i < 0 || i >= n || j < 0 || j >= m || ta[i][j] == '.') return; ta[i][j] = '.'; for (int k = 0; k < 4; k++) { dfs(i + dx[k], j + dy[k], n, m, ta); } }
intmain(){ int n, m; cin >> n >> m; vector<string> ta(n); for(int i = 0; i < n; i++) { cin >> ta[i]; } int ans = 0; for(int i = 0; i < n; i++) { for(int j = 0; j < m; j++) { if(ta[i][j] == 'W') { dfs(i, j, n, m, ta); ans++; } } } cout << ans << endl; return0; }
for (int i = 0; i < n; ++i) { // 0/1 背包倒序枚举容量,避免同一层重复使用第 i 件物品 for (int cap = V; cap >= w[i]; --cap) { dp[cap] = max(dp[cap], dp[cap - w[i]] + (longlong)v[i]); } }
// a: 1..n(a[0] 可忽略);m: 选 m 段互不相交子段,使总和最大 ll maxMSubarraySum(const vector<ll>& a, int m){ int n = (int)a.size() - 1; const ll NEG = (ll)-4e18;
// g[i] = 用前 i 个数选 (k-1) 段的最大和(上一次循环的结果) // f[i] = 用前 i 个数选 k 段的最大和(本次循环) vector<ll> g(n+1, 0), f(n+1, NEG);
for (int k = 1; k <= m; ++k) { fill(f.begin(), f.end(), NEG);
ll bestEnd = NEG; // 第 k 段“必须以 i 结尾”的最佳值 // i 至少为 k(保证能取到 k 段) for (int i = k; i <= n; ++i) { // 要么把第 k 段继续延伸到 a[i],要么在 i 启动新段(接在 g[i-1] 后) bestEnd = max(bestEnd + a[i], g[i-1] + a[i]);
// f[i] = 用前 i 个数选 k 段的最大值:要么不选 a[i] 结尾(承接 f[i-1]),要么选 f[i] = (i == k) ? bestEnd : max(f[i-1], bestEnd); }
#include<bits/stdc++.h> usingnamespace std; using ll = longlong;
// 计算前缀和 P[0..n] staticinline vector<ll> prefix(const vector<ll>& a){ int n = (int)a.size(); vector<ll> P(n+1, 0); for(int i = 1; i <= n; ++i) P[i] = P[i-1] + a[i-1]; return P; }
// 1) 最大子段和,子段长度 ≤ m // 维护窗口 [r-m, r-1] 内 prefix 的最小值 -> deque 单调递增(存下标) ll maxSubarrayLenAtMost(const vector<ll>& a, int m){ int n = (int)a.size(); auto P = prefix(a); deque<int> dq; // 存候选 l 的下标,保证 P[dq[0]] 最小 dq.push_back(0); ll ans = LLONG_MIN; for(int r = 1; r <= n; ++r){ // 丢掉窗口外 l < r-m while(!dq.empty() && dq.front() < r - m) dq.pop_front(); // 用当前最小前缀更新答案 ans = max(ans, P[r] - P[dq.front()]); // 把 r 作为未来的 l 加入,维持 P 递增 while(!dq.empty() && P[dq.back()] >= P[r]) dq.pop_back(); dq.push_back(r); } return ans; }
// 2) 最大子段和,子段长度 ≥ m // 对每个 r(≥m),取 bestMin = min_{l≤r-m} P[l] ll maxSubarrayLenAtLeast(const vector<ll>& a, int m){ int n = (int)a.size(); auto P = prefix(a); ll ans = LLONG_MIN, bestMin = LLONG_MAX; for(int r = 1; r <= n; ++r){ if(r - m >= 0) bestMin = min(bestMin, P[r - m]); // 更新允许的最小前缀 if(r >= m) ans = max(ans, P[r] - bestMin); } return ans; }
// (赠)3) 最大子段和,子段长度 = m ll maxSubarrayLenExactly(const vector<ll>& a, int m){ int n = (int)a.size(); auto P = prefix(a); ll ans = LLONG_MIN; for(int r = m; r <= n; ++r) ans = max(ans, P[r] - P[r - m]); return ans; }
#include<bits/stdc++.h> usingnamespace std; using ll = longlong;
structRect { ll sum; int top, bot, lef, rig; };
// Kadane,顺便恢复左右端点 staticinline pair<ll, pair<int,int>> kadane_with_pos(const vector<ll>& a){ ll best = LLONG_MIN, cur = 0; int bestL = 0, bestR = -1, curL = 0; for (int r = 0; r < (int)a.size(); ++r) { if (cur <= 0) { cur = a[r]; curL = r; } else { cur += a[r]; } if (cur > best) { best = cur; bestL = curL; bestR = r; } } return {best, {bestL, bestR}}; }
// 最大子矩阵和(返回和与矩形坐标,0-index) Rect max_submatrix_sum(const vector<vector<int>>& A){ int n = (int)A.size(), m = (int)A[0].size(); Rect ans{LLONG_MIN, 0,0,0,0}; vector<ll> col(m, 0);
for (int top = 0; top < n; ++top) { fill(col.begin(), col.end(), 0); for (int bot = top; bot < n; ++bot) { for (int c = 0; c < m; ++c) col[c] += A[bot][c]; auto [best, LR] = kadane_with_pos(col); if (best > ans.sum) ans = {best, top, bot, LR.first, LR.second}; } } return ans; }
intmain(){ ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; // 如果是“黑白差值”:把 '1' 映射为 +1,'0' 映射为 -1 vector<vector<int>> a(n, vector<int>(m)); for (int i = 0; i < n; ++i){ string s; cin >> s; for (int j = 0; j < m; ++j) a[i][j] = (s[j] == '1' ? +1 : -1); } auto res = max_submatrix_sum(a); cout << res.sum << "\n"; // 最大子矩阵和 // 如需坐标:cout << res.top << " " << res.bot << " " << res.lef << " " << res.rig << "\n"; return0; }
// f(i,j): a 的前 i 个字符 和 b 的前 j 个字符 的 LCS 长度 intdfs(const string& a, int i, const string& b, int j, vector<vector<int>>& memo){ if (i == 0 || j == 0) return0; int &res = memo[i][j]; if (res != -1) return res; if (a[i-1] == b[j-1]) return res = 1 + dfs(a, i-1, b, j-1, memo); return res = max(dfs(a, i-1, b, j, memo), dfs(a, i, b, j-1, memo)); }
intLCS_len_dfs(const string& a, const string& b){ int n = (int)a.size(), m = (int)b.size(); vector<vector<int>> memo(n+1, vector<int>(m+1, -1)); returndfs(a, n, b, m, memo); }
本题采用动态规划的方法,首先将给定数组进行排序(从小到大排序),其次依次遍历排序后的数组,当访问第 i 个元素时计算 diff = a [i]-a [i-1],即 a [i-1] 和 a [i] 之间的差值。因为每次操作元素只能增加 1,所以操作次数 move += diff。按照题目要求每次将会操作 n-1 个元素,因此当遍历到第 i 个元素时,除了前 i-1 个元素要增加 diff,后面的 a [j] += diff(其中 j > i && j < n)。实际上我们可以将 a [j] 的增幅,累积到 move 上面,当每次遍历到第 i 个元素时,先更新 a [i] += move 再计算 diff,这样就不需要每次更新 a [j] 的值。遍历结束后 move 的值即为最小的操作次数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
classSolution { public: intminMoves(vector<int>& nums){ int n = nums.size(); sort(nums.begin(), nums.end()); int diff = 0; int move = 0; for(int i = 1; i < n; ++i) { nums[i] += move; diff = nums[i] - nums[i-1]; move += diff; } return move; } };
// 节点属性(1..tot),0 表示空 int n = 0, tot = 0; vector<int> lc, rc, val; // 左儿子、右儿子、节点值 unordered_map<int,int> pos; // 值 -> inorder 位置,值唯一
voidinit(int n_){ n = n_; tot = 0; lc.assign(n+1, 0); rc.assign(n+1, 0); val.assign(n+1, 0); pos.clear(); pos.reserve(n*2+1); }
// ====== 构树(前序 + 中序)====== // 入参:pre[0..n-1], in[0..n-1];返回:根下标(int) intbuildFromPreIn(const vector<int>& pre, const vector<int>& in){ init((int)pre.size()); for(int i=0;i<(int)in.size();++i) pos[in[i]]=i; returnbuildPreIn(pre, 0, (int)pre.size()-1, in, 0, (int)in.size()-1); } intbuildPreIn(const vector<int>& pre, int pl, int pr, const vector<int>& in, int il, int ir){ if(pl>pr) return0; int rootVal = pre[pl]; int k = pos[rootVal]; // root 在中序中的位置 int L = k - il; // 左子树大小 int u = ++tot; val[u]=rootVal; lc[u] = buildPreIn(pre, pl+1, pl+L, in, il, k-1); rc[u] = buildPreIn(pre, pl+L+1, pr, in, k+1, ir); return u; }
// ====== 构树(中序 + 后序)====== intbuildFromInPost(const vector<int>& in, const vector<int>& post){ init((int)in.size()); for(int i=0;i<(int)in.size();++i) pos[in[i]]=i; returnbuildInPost(in, 0, (int)in.size()-1, post, 0, (int)post.size()-1); } intbuildInPost(const vector<int>& in, int il, int ir, const vector<int>& post, int pl, int pr){ if(il>ir) return0; int rootVal = post[pr]; int k = pos[rootVal]; int L = k - il; int u = ++tot; val[u]=rootVal; lc[u] = buildInPost(in, il, k-1, post, pl, pl+L-1); rc[u] = buildInPost(in, k+1, ir, post, pl+L, pr-1); return u; }
cin >> n; edges.reserve(n - 1); for (int i = 1; i < n; ++i) { int u, v; longlong w; cin >> u >> v >> w; edges.push_back({u, v, w}); int id = (int)edges.size() - 1; G[u].push_back(id); G[v].push_back(id); }
// 区间第 k 小(基于预先准备好的 vals 离散值域进行二分) ll kth(int L, int R, int k)const{ // 假设 1 <= k <= R-L+1 int lo = 0, hi = (int)vals.size()-1; ll ans = vals.back(); while(lo <= hi){ int mid = (lo + hi) >> 1; ll x = vals[mid]; if(count_le(L, R, x) >= k){ ans = x; hi = mid-1; } else lo = mid+1; } return ans; } };
// 在线处理 for(auto &tp : ops){ int op = get<0>(tp); if(op==1){ int i = get<1>(tp); ll v = get<2>(tp); ds.update(i, v); }else{ int LR = get<1>(tp), k = (int)get<2>(tp); int L = LR>>16, R = LR & 0xFFFF; cout << ds.kth(L, R, k) << "\n"; } } return0; }
int n, m; vector<int> G[MAXN]; int in[MAXN]; // 存储每个结点的入度
booltoposort(){ vector<int> L; queue<int> S; for (int i = 1; i <= n; i++) if (in[i] == 0) S.push(i); while (!S.empty()) { int u = S.front(); S.pop(); L.push_back(u); for (auto v : G[u]) { if (--in[v] == 0) { S.push(v); } } } if (L.size() == n) { for (auto i : L) cout << i << ' '; returntrue; } returnfalse; }
// 或者下面留一个可能的拓扑序在一个 topo 里
booltoposort(vector<int>& topo){ queue<int> q; for (int i = 1; i <= n; i++) if (in[i] == 0) q.push(i); // 入度为 0 的点先入队
while (!q.empty()) { int u = q.front(); q.pop(); topo.push_back(u); for (auto v : G[u]) { if (--in[v] == 0) q.push(v); } } return topo.size() == n; }
longlong T = 0; // 汇事件(出度为 0)的 ve 最大值即总工期 for (int i = 1; i <= n; i++) { if (g[i].empty()) T = max(T, ve[i]); }
// 逆推 vl:汇事件 vl = T for (int i = 1; i <= n; i++) { if (g[i].empty()) vl[i] = T; } for (int k = (int)topo.size() - 1; k >= 0; --k) { int u = topo[k]; for (auto [v, id] : g[u]) { vl[u] = min(vl[u], vl[v] - E[id].w); } // 对仅作为源、无后继且未被设置的点,令 vl[u] = ve[u] if (vl[u] == LLONG_MAX / 4) vl[u] = ve[u]; }
// 关键活动:e == l vector<int> critical_edges; for (int id = 0; id < (int)E.size(); id++) { int u = E[id].u, v = E[id].v; longlong w = E[id].w; longlong e = ve[u]; longlong l = vl[v] - w; if (e == l) critical_edges.push_back(id); // 0-based 的边编号 } return {T, critical_edges}; } };
for (int i = 0; i < n; ++i) { if (!vis[i] && dfs(i)) returntrue; } returnfalse; } // 无向图 DSU 加速 boolhas_cycle_union_find(int n, const vector<pair<int,int>>& edges){ DSU dsu(n); for (auto [u, v] : edges) { int ru = dsu.find(u), rv = dsu.find(v); if (ru == rv) returntrue; // 同一集合再连边 => 有环 dsu.unite(ru, rv); } returnfalse; }
// ---------- 3) 独立环个数(cyclomatic number = m - n + k) ---------- intcyclomatic_number(int n, const vector<pair<int,int>>& edges){ auto adj = build_adj(n, edges, /*directed=*/false); int m = 0; for (int i = 0; i < n; ++i) m += (int)adj[i].size(); m /= 2; // 无向边数
vector<char> vis(n, 0); int k = 0; for (int i = 0; i < n; ++i) if (!vis[i]) { k++; // 迭代 DFS/BFS 统计连通分量 stack<int> st; st.push(i); vis[i] = 1; while (!st.empty()) { int u = st.top(); st.pop(); for (int v : adj[u]) if (!vis[v]) vis[v] = 1, st.push(v); } } return m - n + k; }
int n, m; vector<edge> e[MAXN]; vector<int> L; // 存储拓扑排序结果 int max_dis[MAXN], min_dis[MAXN], in[MAXN]; // in 存储每个节点的入度
voidtoposort(){ // 拓扑排序 queue<int> S; memset(in, 0, sizeof(in)); for (int i = 1; i <= n; i++) { for (int j = 0; j < e[i].size(); j++) { in[e[i][j].v]++; } } for (int i = 1; i <= n; i++) if (in[i] == 0) S.push(i); while (!S.empty()) { int u = S.front(); S.pop(); L.push_back(u); for (int i = 0; i < e[u].size(); i++) { if (--in[e[u][i].v] == 0) { S.push(e[u][i].v); } } } }
voiddp(int s){ // 以 s 为起点求单源最长(短)路 toposort(); // 先进行拓扑排序 memset(min_dis, 0x3f, sizeof(min_dis)); memset(max_dis, 0, sizeof(max_dis)); min_dis[s] = 0; for (int i = 0; i < L.size(); i++) { int u = L[i]; for (int j = 0; j < e[u].size(); j++) { min_dis[e[u][j].v] = min(min_dis[e[u][j].v], min_dis[u] + e[u][j].w); max_dis[e[u][j].v] = max(max_dis[e[u][j].v], max_dis[u] + e[u][j].w); } } }
// 进行 n-1 轮松弛 for (int i = 1; i <= n - 1; ++i) { bool any = false; for (constauto& e : edges) { if (dist[e.u] == INF) continue; if (dist[e.u] + e.w < dist[e.v]) { dist[e.v] = dist[e.u] + e.w; pre[e.v] = e.u; any = true; } } if (!any) break; // 提前退出 }
// 第 n 轮检测是否仍能松弛(是否存在从 s 可达的负环) for (constauto& e : edges) { if (dist[e.u] == INF) continue; if (dist[e.u] + e.w < dist[e.v]) { returnfalse; // 有可达负环 } } returntrue; }
// 从 s 到 t 的最短路径(若不可达返回空向量) vector<int> get_path(int s, int t, const vector<int>& pre){ vector<int> path; if (t < 0 || t >= (int)pre.size()) return path; if (pre[t] == -1 && s != t) return path; // 不可达(且不是 s 自身) for (int v = t; v != -1; v = pre[v]) path.push_back(v); reverse(path.begin(), path.end()); if (!path.empty() && path.front() == s) return path; return {}; // 保险:首点不是 s 则判为不可达 }
vector<Edge> edges; edges.reserve(m); for (int i = 0; i < m; ++i) { int u, v; longlong w; cin >> u >> v >> w; edges.push_back({u, v, w}); // 无向图:再加一条 edges.push_back({v, u, w}); }
while (!q.empty()) { int u = q.front(); q.pop(); inq[u] = 0;
for (auto &p : adj[u]) { int v = p.first; longlong w = p.second; if (dist[u] != INF && dist[u] + w < dist[v]) { dist[v] = dist[u] + w; pre[v] = u; if (!inq[v]) { q.push(v); inq[v] = 1; if (++cnt[v] > n) { // 从 s 可达的负环 returnfalse; } } } } } returntrue; // 无从 s 可达的负环 }
// 从 s 到 t 的最短路径(若不可达返回空向量) vector<int> get_path(int s, int t, const vector<int>& pre){ vector<int> path; if (t < 0 || t >= (int)pre.size()) return path; if (pre[t] == -1 && s != t) return path; // 不可达(且不是 s 自身) for (int v = t; v != -1; v = pre[v]) path.push_back(v); reverse(path.begin(), path.end()); if (!path.empty() && path.front() == s) return path; return {}; // 保险:首点不是 s 则判为不可达 }
structEdge { int u, v; longlong w; }; constlonglong INF = (1LL<<62);
// 进行“至多 K+1 条边”的层次化 Bellman-Ford // 输出:dist, pre(pre[v] 为到达 v 的前驱;不可达保持 -1) voidlimited_bf_with_path(int n, int s, const vector<Edge>& edges, int K, vector<longlong>& dist, vector<int>& pre){ vector<longlong> dist_old(n+1, INF), dist_new(n+1, INF); vector<int> pre_old(n+1, -1), pre_new(n+1, -1);
dist_old[s] = 0; pre_old[s] = -1;
// 逐层扩展,每层增加 1 条可用边,最多 K+1 条边 for (int i = 1; i <= K + 1; ++i) { dist_new = dist_old; // 关键:先复制,防止同层再传递 pre_new = pre_old;
for (constauto& e : edges) { if (dist_old[e.u] == INF) continue; // 上一层不可达 longlong cand = dist_old[e.u] + e.w; // 只能用上一层的值 if (cand < dist_new[e.v]) { dist_new[e.v] = cand; pre_new[e.v] = e.u; // 记录前驱(来自上一层) } } dist_old.swap(dist_new); pre_old.swap(pre_new); }
dist = move(dist_old); pre = move(pre_old); }
// 从 s 到 t 恢复路径(若不可达返回空向量) vector<int> get_path(int s, int t, const vector<int>& pre){ vector<int> path; if (t < 0 || t >= (int)pre.size()) return path; if (s == t) return {s}; if (pre[t] == -1) return path; // 不可达 for (int v = t; v != -1; v = pre[v]) path.push_back(v); reverse(path.begin(), path.end()); if (!path.empty() && path.front() == s) return path; return {}; // 保险:首点不是 s 则判不可达 }
intmain(){ ios::sync_with_stdio(false); cin.tie(nullptr); int n, m, s, t, K; // 输入:n 点,m 边,起点 s, 终点 t, 中转城市上限 K(即最多 K+1 条边) // 例:n m s t K // m 行:u v w if (!(cin >> n >> m >> s >> t >> K)) return0;
vector<Edge> edges; edges.reserve(m); for (int i = 0; i < m; ++i) { int u, v; longlong w; cin >> u >> v >> w; edges.push_back({u, v, w}); // 无向图:edges.push_back({v, u, w}); }
// 自环距离为 0 for (int i = 1; i <= n; ++i) { dist[i][i] = 0; nxt[i][i] = i; // 恢复 i->i 时用得到(可选) }
// 读入无向边 for (int i = 0; i < m; ++i) { int u, v; longlong w; cin >> u >> v >> w; if (w < dist[u][v]) { // 若有重边取更小 dist[u][v] = dist[v][u] = w; nxt[u][v] = v; nxt[v][u] = u; } }
// Floyd–Warshall with path reconstruction for (int k = 1; k <= n; ++k) { for (int i = 1; i <= n; ++i) { if (dist[i][k] == INF) continue; for (int j = 1; j <= n; ++j) { if (dist[k][j] == INF) continue; longlong cand = dist[i][k] + dist[k][j]; if (cand < dist[i][j]) { dist[i][j] = cand; nxt[i][j] = nxt[i][k]; // 关键:第一跳来自 i->k 的第一跳 } } } }
// 查询:既返回距离也能打印路径 int q; cin >> q; while (q--) { int s, t; cin >> s >> t;
// 还想要路径的话: vector<int> path; int cur = s; path.push_back(cur); // 防御:最多走 n 步避免意外环 for (int step = 0; cur != t && step <= n; ++step) { cur = nxt[cur][t]; if (cur == -1) break; path.push_back(cur); }
// 输出距离 cout << dist[s][t] << "\n";
// 如需打印路径,取消注释: // for (int i = 0; i < (int)path.size(); ++i) { // cout << path[i] << (i + 1 == (int)path.size() ? '\n' : ' '); // } }
#include<iostream> #include<queue> #include<string.h> usingnamespace std; int moves[1001][1001]; int dir[8][2]={-2,-1,-2,1,-1,2,1,2,2,1,2,-1,1,-2,-1,-2}; int b1, b2; // F = G + H // G = 从起点到该节点路径消耗 // H = 该节点到终点的预估消耗
structKnight{ int x,y; int g,h,f; booloperator < (const Knight & k) const{ // 重载运算符,从小到大排序 return k.f < f; } };
for (int it = 1; it <= n; ++it) { int u = -1; ll best = INF; for (int v = 1; v <= n; ++v) if (!vis[v] && dis[v] < best) { best = dis[v]; u = v; } if (u == -1 || best == INF) { picked.clear(); return-1; } // 不连通 vis[u] = 1; total += dis[u]; if (par[u] != 0) picked.push_back({par[u], u, dis[u]}); for (auto e : g[u]) if (!vis[e.v] && e.w < dis[e.v]) { dis[e.v] = e.w; par[e.v] = u; } } return total; }
int n, m; cin >> n >> m; vector<Edge> es(m); vector<vector<Adj>> g(n+1); for (int i=0;i<m;++i){ int u,v; ll w; cin >> u >> v >> w; g[u].push_back({v,w}); // 给 Prim g[v].push_back({u,w}); }
// 枚举所有非空子集 for (int S = 1; S <= FULL; ++S) { // 1) 在同一根 i 上合并子集(S 的真子集 T) for (int i = 1; i <= n; ++i) { // 枚举 S 的真子集 T(T != 0 && T != S),允许重复考虑,取 min 即可 // T = (T-1) & S 是经典子集枚举 for (int T = (S-1)&S; T; T = (T-1)&S) { dp[i][S] = min(dp[i][S], dp[i][T] + dp[i][S^T]); } }
// 2) 固定 S,做一遍「多源 Dijkstra」传播到整图 priority_queue<pair<ll,int>, vector<pair<ll,int>>, greater<pair<ll,int>>> pq; for (int i = 1; i <= n; ++i) { if (dp[i][S] < INF) pq.emplace(dp[i][S], i); } while (!pq.empty()) { auto [du, u] = pq.top(); pq.pop(); if (du != dp[u][S]) continue; // 过期 for (auto e : g[u]) { if (dp[e.to][S] > du + e.w) { dp[e.to][S] = du + e.w; pq.emplace(dp[e.to][S], e.to); } } } }
// 答案:min_i dp[i][FULL] ll ans = INF; for (int i = 1; i <= n; ++i) ans = min(ans, dp[i][FULL]); return ans; }
int n, m; cin >> n >> m; for (int i = 0; i < m; ++i){ int u, v; cin >> u >> v; // 有向边 u -> v add_edge(u, v); }
// 跑 Tarjan for (int u = 1; u <= n; ++u) if (!dfn[u]) tarjan(u);
// 1) 强连通分量个数 cout << scc_cnt << "\n";
// 2) 每个点属于哪个 SCC(如需) // for (int u = 1; u <= n; ++u) cout << scc_id[u] << (u==n?'\n':' ');
// 3) (可选)凝聚图入度/出度统计示例 // vector<int> indeg(scc_cnt+1), outdeg(scc_cnt+1); // for (int u = 1; u <= n; ++u) // for (int i = head[u]; i; i = e[i].nxt){ // int v = e[i].to; // int a = scc_id[u], b = scc_id[v]; // if (a != b){ outdeg[a]++; indeg[b]++; } // } // // 例:判断整图是否强连通:if (scc_cnt == 1) ...
for (int v : g[u]) { if (dfn[v] == -1) { // tree edge dfs(v); low[u] = min(low[u], low[v]); } elseif (in_st[v]) { // back/forward/cross to stack => shrink low[u] = min(low[u], dfn[v]); } }
// u is root of an SCC if (low[u] == dfn[u]) { int sz = 0; while (true) { int x = st.back(); st.pop_back(); in_st[x] = 0; ++sz; if (x == u) break; } if (sz >= 2) has_cycle = true; // 一个 SCC 含 ≥2 个点 ⇒ 必有环 } }
boolrun_and_check_cycle(){ if (seen_self_loop) returntrue; // 提前剪枝 for (int i = 0; i < n && !has_cycle; ++i) if (dfn[i] == -1) dfs(i); return has_cycle; } };
// 基本加边 voidadd_imp(int u, int v){ g[u].push_back(v); } // u -> v voidadd_or(int a, int b){ add_imp(neg(a), b); add_imp(neg(b), a); } // (a ∨ b) voidadd_true(int a){ add_imp(neg(a), a); } // a 为真 voidadd_false(int a){ add_imp(a, neg(a)); } // a 为假 voidadd_eq(int a, int b){ add_imp(a,b); add_imp(b,a); add_imp(neg(a),neg(b)); add_imp(neg(b),neg(a)); } voidadd_xor(int a, int b){ add_or(a,b); add_or(neg(a),neg(b)); } // a ⊕ b
// 加边:有向=单向;无向=双向(共享同一 eid) voidaddEdge(int u, int v){ int id = M++; if ((int)used.size() < M) used.resize(M, 0); adj[u].push_back({v,id}); if (directed){ outdeg[u]++; indeg[v]++; } else { adj[v].push_back({u,id}); deg[u]++; deg[v]++; } }
voidsort_adj(){ for (int u=1; u<=n; ++u) sort(adj[u].begin(), adj[u].end(), [](constauto& A, constauto& B){ return A.first < B.first; }); }
boolfeasible(){ // 度数可行性快速判断 if (directed){ int plus1=0, minus1=0; for (int u=1; u<=n; ++u){ int d = outdeg[u]-indeg[u]; if (d==1) ++plus1; elseif (d==-1) ++minus1; elseif (d!=0) returnfalse; } return (M==0) || (plus1==0&&minus1==0) || (plus1==1&&minus1==1); } else { int odd=0; for (int u=1; u<=n; ++u) if (deg[u]&1) ++odd; return (M==0) || (odd==0 || odd==2); } }
intpick_start(){ // 选起点(字典序最小) if (directed){ for (int u=1; u<=n; ++u) if (outdeg[u]-indeg[u]==1) return u; for (int u=1; u<=n; ++u) if (outdeg[u]>0) return u; } else { for (int u=1; u<=n; ++u) if (deg[u]&1) return u; for (int u=1; u<=n; ++u) if (deg[u]>0) return u; } return1; // 无边时随便 }
voiddfs(int u){ auto &i = it[u]; while (i < (int)adj[u].size()){ auto [v,id] = adj[u][i++]; if (used[id]) continue; used[id] = 1; // 无向图两方向共用同一 eid → 一次标记解决 dfs(v); } path.push_back(u); }
// 返回顶点序列;空=不存在或未覆盖所有边(不连通等) vector<int> run(bool lex_smallest=true){ path.clear(); fill(it.begin(), it.end(), 0); if (!feasible()) return {}; if (lex_smallest) sort_adj(); if (M==0) return {}; // 没有边时按题意自己处理 int s = pick_start(); dfs(s); if ((int)path.size() != M+1) return {}; // 没走完所有边 reverse(path.begin(), path.end()); return path; } };
int n = 500; // 或者“读到的最大点号” int m; cin >> m; Euler G(n, /*directed=*/false); for (int i=0;i<m;++i){ int u,v; cin>>u>>v; G.addEdge(u,v); } auto ans = G.run(true); // 字典序最小 for (int v: ans) cout << v << "\n"; // 每行一个点
int nL, nR; // 左右部点数 Dinic D(nL + nR + 2); int S = nL + nR, T = S + 1; for(int u=1; u<=nL; ++u) D.addEdge(S, u-1, 1); for(int v=1; v<=nR; ++v) D.addEdge(nL+v-1, T, 1); // 有边的 (u in L, v in R): // D.addEdge(u-1, nL+v-1, 1); longlong M = D.maxflow(S,T); // 即最大匹配数
auto side = D.mincut_reachable(S); // (u in S and edge u->v 满流) 即是最小割边
指定了源点 s 和汇点 t,要你 “切断 s 到 t 的最小代价”,用 Dinic 就行。
把题目抽象成 “切断 s 和 t 的最小代价”,确定是 边代价 还是 点代价(点代价→拆点)。
容量 = 代价;需要 “不可通过” 的地方用 ∞。
多源多汇 → 加超级源 / 汇。
maxflow(s,t) 得数值; mincut_reachable(s) 拿到 S 集合,从而恢复 “要切的边 / 点”。
for (int phase = 1; phase < n; ++phase) { fill(added.begin(), added.end(), 0); vector<ll> dist(n+1, 0); int s = -1, t = -1;
for (int it = 1; it <= n - phase + 1; ++it) { int sel = -1; for (int v = 1; v <= n; ++v) if (exist[v] && !added[v] && (sel == -1 || dist[v] > dist[sel])) sel = v;
if (it == n - phase + 1) { // 最后加入的点 = t,倒数第二个 = s t = sel; s = prev[sel];
// 本轮 s-t 最小割值 = dist[t] if (dist[t] < best) { best = dist[t]; if (outS) { // 记录此轮的 A\{t} 作为一侧 vector<int> S; for (int v = 1; v <= n; ++v) if (exist[v] && added[v] && v != t) S.push_back(v); bestS.swap(S); } }
// 合并 t -> s if (s == -1) { exist[t] = 0; break; } // n=1 时 exist[t] = 0; for (int v = 1; v <= n; ++v) if (exist[v] && v != s) { w[s][v] += w[t][v]; w[v][s] += w[v][t]; } } else { added[sel] = 1; for (int v = 1; v <= n; ++v) if (exist[v] && !added[v]) { dist[v] += w[sel][v]; prev[v] = sel; } } } } if (outS) *outS = bestS; return (best == (1LL<<60) ? 0 : best); // 不连通时答案为 0 }
int n, m, s, t; cin >> n >> m >> s >> t; MCMF mf(n+1); // 点编号 1..n for (int i=0;i<m;i++){ int u,v; longlong cap, cost; cin >> u >> v >> cap >> cost; mf.addEdge(u,v,cap,cost); // 有向 } auto [maxflow, mincost] = mf.minCostMaxFlow(s,t); cout << maxflow << " " << mincost << "\n"; }
例题:G 公司有 n 个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量不等。如何用最少搬运量可以使 n 个仓库的库存数量相同。搬运货物时,只能在相邻的仓库之间搬运。
booldfs(int u){ for (int v : g[u]) if (seen[v] != visToken) { seen[v] = visToken; if (matchR[v] == 0 || dfs(matchR[v])) { matchR[v] = u; returntrue; } } returnfalse; }
// 返回最大匹配数,以及匹配对 (u,v) pair<int, vector<pair<int,int>>> maxMatching() { int match = 0; for (int u = 1; u <= nL; ++u) { ++visToken; if (dfs(u)) ++match; } vector<pair<int,int>> pairs; for (int v = 1; v <= nR; ++v) if (matchR[v]) pairs.push_back({matchR[v], v}); return {match, pairs}; } };
/* 用法 int nL,nR,m; cin>>nL>>nR>>m; HungarianKuhn hk(nL,nR); while(m--){ int u,v; cin>>u>>v; hk.addEdge(u,v); } auto [cnt, pairs] = hk.maxMatching(); cout<<cnt<<"\n"; */
voidsetWeight(int i, int j, longlong val){ w[i][j] = val; }
// 主过程:O(n^3) pair<longlong, vector<int>> solve() { // 初始化顶标 lx 为该行最大权,ly=0 for (int i = 1; i <= n; ++i) { lx[i] = w[i][1]; for (int j = 2; j <= n; ++j) lx[i] = max(lx[i], w[i][j]); } for (int i = 1; i <= n; ++i) { fill(slack.begin(), slack.end(), (longlong)4e18); fill(vx.begin(), vx.end(), false); fill(vy.begin(), vy.end(), false); fill(pre.begin(), pre.end(), 0);
int y0 = 0; // 当前“未匹配的右部点”指针 matchR[0] = i; // 让 0 当作虚拟右点 do { vx[ matchR[y0] ] = true; longlong delta = (longlong)4e18; int x0 = matchR[y0], y1 = 0; for (int y = 1; y <= n; ++y) if (!vy[y]) { longlong gap = lx[x0] + ly[y] - w[x0][y]; if (gap < slack[y]) { slack[y] = gap; pre[y] = y0; } if (slack[y] < delta) { delta = slack[y]; y1 = y; } } // 调整顶标 for (int y = 0; y <= n; ++y) { if (vx[ matchR[y] ]) lx[ matchR[y] ] -= delta; if (vy[y]) ly[y] += delta; else slack[y] -= delta; } y0 = y1; vy[y0] = true; } while (matchR[y0] != 0); // 找到一条到未匹配右点的增广路
// 回溯增广 while (y0) { int yPrev = pre[y0]; matchR[y0] = matchR[yPrev]; y0 = yPrev; } } longlong best = 0; for (int y = 1; y <= n; ++y) best += w[ matchR[y] ][ y ]; return {best, matchR}; // matchR[j] = i } };
/* 用法(最小化成本示例) int n; cin>>n; HungarianKM km(n); long long C = 0; // 若有负值,可先遍历找最小值 minW,再令 C = -minW,填 w'=w+C for (int i=1;i<=n;i++) for (int j=1;j<=n;j++){ long long cost; cin>>cost; km.setWeight(i,j, -cost); // 直接取负做“最大化”,或做平移后取负 } auto [maxW, matchR] = km.solve(); long long minCost = -maxW; cout << minCost << "\n"; // 配对:右部 j 匹配左部 matchR[j] */
// 统计预染色数量 for (int v = 1; v <= n; ++v) if (col[v] > 0) { taken[v] = 1; ++painted; }
int used_colors = 0;
// 先把已有颜色的最大值作为起点(兼容预染色) for (int v = 1; v <= n; ++v) if (col[v] > 0) used_colors = max(used_colors, col[v]);
// 主循环:每一轮铺一种新颜色 for (int idx = 0; idx < n; ++idx) { int v = ord[idx]; if (taken[v]) continue; // 预染色或已上色
int c = ++used_colors; fill(forbid.begin(), forbid.end(), 0);
// 先把所有已是颜色 c 的点(可能来自预染色)标注 forbid 邻居 for (int x = 1; x <= n; ++x) if (col[x] == c) { for (int u : g[x]) forbid[u] = 1; }
// 选第一个点 v(若它不被 forbid) if (!forbid[v]) { col[v] = c; taken[v] = 1; ++painted; for (int u : g[v]) forbid[u] = 1; } else { // v 不能用本色,跳过;让后续点来铺这一色 }
// 扫后续,能上 c 的都上 for (int j = idx + 1; j < n; ++j) { int w = ord[j]; if (taken[w] || forbid[w]) continue; col[w] = c; taken[w] = 1; ++painted; for (int u : g[w]) forbid[u] = 1; } if (painted == n) break; }
// 可选:汇总颜色分组 if (groups) { groups->assign(used_colors + 1, {}); for (int v = 1; v <= n; ++v) if (col[v] > 0) (*groups)[col[v]].push_back(v); } return used_colors; }
// ====== 合法性校验:所有边的两端颜色不同 ====== boolCheckColoring(int n, const vector<vector<int>>& g, const vector<int>& col){ for (int u = 1; u <= n; ++u) for (int v : g[u]) if (u < v) { if (col[u] == 0 || col[v] == 0) returnfalse; if (col[u] == col[v]) returnfalse; } returntrue; }
int n, m, total; int deg[200001], u[200001], v[200001]; bool vis[200001];
structEdge { int to, nxt; } edge[200001];
int cntEdge, head[100001];
voidaddEdge(int u, int v){ edge[++cntEdge] = {v, head[u]}, head[u] = cntEdge; }
intmain(){ cin.tie(nullptr)->sync_with_stdio(false); cin >> n >> m; for (int i = 1; i <= m; i++) cin >> u[i] >> v[i], deg[u[i]]++, deg[v[i]]++; for (int i = 1; i <= m; i++) { if ((deg[u[i]] == deg[v[i]] && u[i] > v[i]) || deg[u[i]] < deg[v[i]]) swap(u[i], v[i]); addEdge(u[i], v[i]); } for (int u = 1; u <= n; u++) { for (int i = head[u]; i; i = edge[i].nxt) vis[edge[i].to] = true; for (int i = head[u]; i; i = edge[i].nxt) { int v = edge[i].to; for (int j = head[v]; j; j = edge[j].nxt) { int w = edge[j].to; if (vis[w]) total++; } } for (int i = head[u]; i; i = edge[i].nxt) vis[edge[i].to] = false; } cout << total << '\n'; return0; }
int n, m, deg[100001], cnt[100001]; vector<int> E[100001], E1[100001];
longlong total;
intmain(){ cin.tie(nullptr)->sync_with_stdio(false); 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); deg[u]++, deg[v]++; } for (int u = 1; u <= n; u++) for (int v : E[u]) if (deg[u] > deg[v] || (deg[u] == deg[v] && u > v)) E1[u].push_back(v); for (int a = 1; a <= n; a++) { for (int b : E1[a]) for (int c : E[b]) { if (deg[a] < deg[c] || (deg[a] == deg[c] && a <= c)) continue; total += cnt[c]++; } for (int b : E1[a]) for (int c : E[b]) cnt[c] = 0; } cout << total << '\n'; return0; }
vector<int> subarrayMajority(vector<int>& nums, vector<vector<int>>& queries){ int n = (int)nums.size(), q = (int)queries.size(); vector<QEx> qs; qs.reserve(q); for (int i = 0; i < q; ++i) { int l = queries[i][0], r = queries[i][1], t = queries[i][2]; if (l > r) swap(l, r); qs.push_back({l, r, t, i}); }
vector< set<int> > bucket(n + 1); // 频次 f -> 值集合 (升序) int curMaxFreq = 0;
auto add_pos = [&](int pos){ int x = nums[pos]; int f = cnt[x]; if (f > 0) { auto &S = bucket[f]; auto it = S.find(x); if (it != S.end()) S.erase(it); } int nf = f + 1; cnt[x] = nf; bucket[nf].insert(x); if (nf > curMaxFreq) curMaxFreq = nf; };
auto remove_pos = [&](int pos){ int x = nums[pos]; int f = cnt[x]; // f >= 1 auto &S = bucket[f]; auto it = S.find(x); if (it != S.end()) S.erase(it);
int nf = f - 1; cnt[x] = nf; if (nf > 0) bucket[nf].insert(x);
while (curMaxFreq > 0 && bucket[curMaxFreq].empty()) curMaxFreq--; };
structDate { int year, month, day; Date(int y, int m, int d): year(y), month(m), day(d) {}
intweek(){ // 今天是星期几?基姆拉尔森公式 int y = year, m = month, d = day; if(m==1||m==2) m+=12,y--; int w = (d+2*m+3*(m+1)/5 + y + y/4 - y/100 + y/400) % 7; return ++w; // w:0:星期一...依此类推 }
HuffmanResult huffman_k_ary(const vector<longlong>& weights, int k){ constint n = (int)weights.size(); if (n == 0) return {0, 0}; // 最小堆:按 (权值升序,深度升序) using P = pair<longlong,int>; priority_queue<P, vector<P>, greater<P>> pq; for (auto w : weights) pq.emplace(w, 0);
__int128 total = 0; // 用 128 位累加更安全 int max_depth = 0;
while ((int)pq.size() > 1) { longlong sum = 0; int dep = 0; for (int i = 0; i < k; ++i) { auto [w, d] = pq.top(); pq.pop(); sum += w; dep = max(dep, d); } total += sum; pq.emplace(sum, dep + 1); } if (!pq.empty()) max_depth = pq.top().second;
// 取模快速幂 intqPower(int a, int n, int mod){ // 按位展开 int ans = 1, base = a; while(n){ if(n & 1) ans *= base; // 若位为 1,说明有权重,需要乘上 base *= base; ans %= mod; base %= mod; n >>= 1; // 右移 1 位 } return ans; }
// 费马小定理 intqBigPower(int a, int n, int p){ // n 为大规模指数,注意这里可能需要改为高精度取模形式 returnqPower(a, n % (p - 1), p); }
using int64 = longlong; using i128 = __int128_t;
// a*b % mod,64 位安全 staticinlineuint64_tmul_mod(uint64_t a, uint64_t b, uint64_t mod){ return (uint64_t)((i128)a * b % mod); }
staticinline int64 mod_pow(int64 a, int64 e, int64 mod){ uint64_t x = (a%mod+mod)%mod, r = 1%mod; while(e){ if(e&1) r = mul_mod(r, x, mod); x = mul_mod(x, x, mod); e >>= 1; } return (int64)r; }
// 计算 [1..num] 之间有多少个能够被 a 或 b 或 c 整除的数字 longf(int num, int a, int b, int c){ long setA = num / a, setB = num / b, setC = num / c; long setAB = num / lcm(a, b); long setAC = num / lcm(a, c); long setBC = num / lcm(b, c); long setABC = num / lcm(lcm(a, b), c); // 集合论定理:A + B + C - A ∩ B - A ∩ C - B ∩ C + A ∩ B ∩ C return setA + setB + setC - setAB - setAC - setBC + setABC; }
// 行列式计算 (高斯消元法) // 输入:n×n 矩阵 a // 输出:det(a) mod MOD intdet_mod(vector<vector<int>> a, int MOD){ int n = (int)a.size(); longlong det = 1; for (int i = 0; i < n; i++) { // 找到当前列的主元(非零) int pivot = -1; for (int j = i; j < n; j++) { if (a[j][i] % MOD != 0) { pivot = j; break; } } if (pivot == -1) return0; // 该列全 0 → det=0
// 如果主元不在第 i 行,交换行 if (pivot != i) { swap(a[pivot], a[i]); det = (MOD - det) % MOD; // 交换行 → det 取负 }
// 当前主元 int inv_pivot = 1; { longlong base = a[i][i]; // 模逆元(费马小定理,MOD 必须是质数) longlong exp = MOD - 2, res = 1; base = (base % MOD + MOD) % MOD; while (exp) { if (exp & 1) res = res * base % MOD; base = base * base % MOD; exp >>= 1; } inv_pivot = (int)res; }
det = det * (a[i][i] % MOD + MOD) % MOD;
// 消去下面的行 for (int j = i + 1; j < n; j++) { if (a[j][i] == 0) continue; longlong factor = 1LL * a[j][i] * inv_pivot % MOD; for (int k = i; k < n; k++) { a[j][k] = (a[j][k] - factor * a[i][k]) % MOD; if (a[j][k] < 0) a[j][k] += MOD; } } } return (det % MOD + MOD) % MOD; }
vector<ll> a(n + 1); for (int i = 1; i <= n; ++i) cin >> a[i];
vector<vector<int>> G(n + 1); vector<int> indeg(n + 1, 0), outdeg(n + 1, 0); for (int i = 0; i < m; ++i) { int u, v; cin >> u >> v; // u 是 v 的前置:u -> v G[u].push_back(v); ++indeg[v]; ++outdeg[u]; }
// ---------- 拓扑排序(Kahn) ---------- queue<int> q; for (int i = 1; i <= n; ++i) if (indeg[i] == 0) q.push(i);
vector<int> topo; topo.reserve(n); while (!q.empty()) { int u = q.front(); q.pop(); topo.push_back(u); for (int v : G[u]) { if (--indeg[v] == 0) q.push(v); } } // 题目保证有解:topo.size()==n
// ---------- 正向 DP:最早开始时间 f ---------- vector<ll> f(n + 1, 0); for (int u : topo) { for (int v : G[u]) { f[v] = max(f[v], f[u] + a[u]); } } ll T = 0; // 最早完成时间 for (int i = 1; i <= n; ++i) T = max(T, f[i] + a[i]);
// ---------- 逆向 DP:最晚开始时间 g(不拖慢 T) ---------- const ll INF = (ll)4e18; vector<ll> g(n + 1, INF); // 无后继的点:最晚开始 = T - a[i] for (int i = 1; i <= n; ++i) if (outdeg[i] == 0) g[i] = T - a[i];
// 逆拓扑序传递:g[u] = min_{v in succ(u)} g[v] - a[u] for (int idx = (int)topo.size() - 1; idx >= 0; --idx) { int u = topo[idx]; if (outdeg[u] > 0) { ll LF = INF; for (int v : G[u]) LF = min(LF, g[v]); g[u] = min(g[u], LF - a[u]); } // 保证区间 [f[i], g[i]] 非空 if (g[u] < f[u]) g[u] = f[u]; }
// ---------- 结果 ---------- cout << T << "\n";
longlong ans = 1; for (int i = 1; i <= n; ++i) { longlong ways = (g[i] - f[i] + 1) % MOD; if (ways < 0) ways += MOD; ans = (ans * ways) % MOD; } cout << ans << "\n"; return0; }