输入 T(≤1e3) 表示 T 组数据。所有数据的 n 之和 ≤2e5,q 之和 ≤2e5。 每组数据输入 n(1≤n≤2e5),q(1≤q≤2e5),长为 n 的字符串 s,长为 n 的字符串 t,都只包含小写英文字母。 然后输入 q 个询问,每个询问输入两个数 L 和 R,表示下标从 L 到 R 的子串 (1≤L≤R≤n) s[L..R] 和 t[L..R]。
voidsolve() { int n,q; cin >> n >> q; string a,b; cin >> a >> b; vector<vector<int>> sum(n+1,vector<int>(26)); for (int i = 0; i < n; i++) { sum[i+1] = sum[i]; sum[i+1][a[i]-'a']++; sum[i+1][b[i]-'a']--; } for (int i = 0; i < q; i++) { int l,r; cin >> l >> r; int ans = 0; for (int i = 0; i < 26; i++) { ans += abs(sum[r][i]-sum[l-1][i]); } cout << ans/2 << endl; } }
auto dfs = [&](int i, int mask, bool isLimit, auto&& self) -> int { int c = __builtin_popcount(mask); if (c > k) { return0; } if (i == s.length()) { if (c < k) { return0; } return1; } if (!isLimit && mask > 0) { int& res = memo[i][c]; if (res >= 0) { return res; } } int res = 0; if (mask == 0) { res += self(i + 1, 0, false, self); } int up = 15; if (isLimit) { if (s[i] <= '9') { up = s[i] - '0'; } else { up = 10 + (s[i] - 'A'); } } int d = 0; if (mask == 0) { d = 1; } for (; d <= up; d++) { res += self(i + 1, mask | (1 << d), isLimit && d == up, self); res %= MOD; } if (!isLimit && mask > 0) { memo[i][c] = res; } return res; };
voidsolve() { int n,k,ban=0,ans=0; cin >> n >> k; vector<pii> a(n); for (int i = 0; i < n; ++i) { cin >> a[i].first >> a[i].second; } k++; for (int i = 30; i >= 0; i--) { if ((k >> i) & 1) { ban ^= (1 << i); int s = 0; for (auto p: a) { if ((p.first&ban) == 0) { s += p.second; } } ans = max(ans, s); } ban ^= 1 << i; } cout << ans << endl; }
structPoint { int x,y; }; vector<Point> points; int n; vi pre_min, pre_max, suf_min, suf_max; boolcheck(int d){ for (int i = 0; i < n; ++i) { int x_i = points[i].x; int y_i = points[i].y;
// 左边区间 x_j <= x_i - d int left = x_i - d; int low = 0, high = i - 1, k = -1; while (low <= high) { int mid = (low + high) / 2; if (points[mid].x <= left) { k = mid; low = mid + 1; } else { high = mid - 1; } } if (k != -1) { if (pre_max[k] >= y_i + d || pre_min[k] <= y_i - d) { returntrue; } }
// 右边区间 x_j >= x_i + d int right = x_i + d; low = i + 1; high = n - 1; int m = n; while (low <= high) { int mid = (low + high) / 2; if (points[mid].x >= right) { m = mid; high = mid - 1; } else { low = mid + 1; } } if (m < n) { if (suf_max[m] >= y_i + d || suf_min[m] <= y_i - d) { returntrue; } } } returnfalse; } voidsolve() { cin >> n; points.resize(n); for (int i = 0; i < n; i++) { cin >> points[i].x >> points[i].y; } sort(points.begin(), points.end(), [](const Point& a,const Point& b) { return a.x < b.x; }); pre_min.resize(n); pre_max.resize(n); suf_min.resize(n); suf_max.resize(n);
pre_min[0] = points[0].y; pre_max[0] = points[0].y; for (int i = 1; i < n; ++i) { pre_min[i] = min(pre_min[i-1], points[i].y); pre_max[i] = max(pre_max[i-1], points[i].y); }
suf_min[n-1] = points[n-1].y; suf_max[n-1] = points[n-1].y; for (int i = n-2; i >= 0; --i) { suf_min[i] = min(suf_min[i+1], points[i].y); suf_max[i] = max(suf_max[i+1], points[i].y); } int max_x = points.back().x - points[0].x; int global_min_y = pre_min.back(); int global_max_y = pre_max.back(); int max_y_diff = global_max_y - global_min_y;
int left = 0, right = max(max_x, max_y_diff); int ans = 0;
while (left <= right) { int mid = (left + right) / 2; if (check(mid)) { ans = mid; left = mid + 1; } else { right = mid - 1; } } cout << ans << endl; }
voidsolve() { int n,m; cin >> n >> m; vector<vector<Edge>> g(n); for (int i = 0; i < m; i++) { int v,w; cin >> v >> w; g[v-1].push_back({w-1,i}); } vector<Edge> from(n); vector<int> dis(n);
auto bfs = [&](int ban) -> int { fill(dis.begin(), dis.end(), -1); dis[0] = 0; queue<int> q; q.push(0); while (!q.empty()) { int v = q.front(); q.pop(); for (constauto& e : g[v]) { int w = e.to; if (e.i != ban && dis[w] < 0) { dis[w] = dis[v] + 1; if (ban < 0) { from[w] = {v, e.i}; } q.push(w); } } } return dis[n-1]; };
int res = bfs(-1); vector<int> ans(m, res); if (res != -1) { for (int v = n - 1; v != 0; v = from[v].to) { ans[from[v].i] = bfs(from[v].i); } } for (int v : ans) { cout << v << endl; } }
输入 n(1≤n≤2e5) 和长为 n 的数组 a(1≤a[i]≤n)。 然后输入一棵无向树的 n-1 条边,节点编号从 1 到 n。 节点 i 的颜色是 a[i]。
定义 f(c) = 包含颜色 c 的简单路径的数目。注:只有 1 个点也算路径。 输出 f(1),f(2),…,f(n)。
Example
输入
1 2 3 4
3 1 21 1 2 2 3
输出
1 2 3
5 4 0
输入
1 2
1 1
输出
1
1
输入
1 2 3
2 1 2 1 2
输出
1 2
2 2
输入
1 2 3 4 5 6
5 1 2345 1 2 2 3 3 4 3 5
输出
1 2 3 4 5
5 8 10 5 5
输入
1 2 3 4 5 6 7 8 9
8 2 7254175 3 1 1 2 2 7 4 5 5 6 6 8 7 8
输出
1 2 3 4 5 6 7 8
18 15 0 14 23 0 23 0
Solution
正难则反,计算不包含颜色 c 的简单路径数。 所有路径数减去不包含颜色 c 的路径数就是答案。
去掉颜色 c 节点后,树分成了若干连通块。 对于大小为 m 的连通块,其中有 m*(m+1)/2 条简单路径。
如何快速计算各个连通块的大小?
如图,去掉粉色节点后,考虑包含节点 y 的连通块,它的大小等于: 子树 y 的大小,减去子树 z1,z2,z3 的大小之和。
如何计算子树 z1,z2,z3 的大小之和? 额外用一个数组 size[c] 记录以颜色 c 为根的子树的大小之和。
这对吗?考虑 x 更上面的节点,这样做 z1,z2,z3 的子树大小岂不是会和 x 的子树大小相加? 为了避免重复累加子树大小,直接用子树 x 的大小「覆盖」子树 z1,z2,z3 的大小之和。 具体来说,在 dfs(x) 的开头,用临时变量 old 记录 size[c];在 dfs(x) 的末尾,覆盖 size[c] = old + 子树 x 的大小。
voidsolve() { int n; cin >> n; vi a(n); for (int i = 0; i < n; i++) { cin >> a[i]; } vector<vector<int>> g(n); for (int i = 1; i < n; ++i) { int v,w; cin >> v >> w; v--; w--; g[v].pb(w); g[w].pb(v); } vi ans(n),size(n); function<int(int, int)> dfs = [&](int v, int fa) -> int { int c = a[v] - 1; int old = size[c]; int sz = 1; for (int w : g[v]) { if (w != fa) { size[c] = 0; int szW = dfs(w, v); sz += szW; int m = szW - size[c]; ans[c] += m * (m + 1) / 2; } } size[c] = old + sz; return sz; };
dfs(0, -1); for (int i = 0; i < n; ++i) { int m = n - size[i]; cout << n * (n + 1) / 2 - ans[i] - m * (m + 1) / 2 << endl; } }
voidsolve() { int n; cin >> n; vi a(n); unordered_map<int, vi> pos_dict; for (int i = 0; i < n; i++) { cin >> a[i]; pos_dict[a[i]].pb(i); } int total_arrays = n * (n + 1) / 2; int result = 0; for (constauto& entry : pos_dict) { const vi& position = entry.second; int prev = -1; int sum_not = 0; for (int p : position) { int start = prev + 1; int end = p - 1; if (start <= end) { int length = end - start + 1; sum_not += length * (length + 1) / 2; } prev = p; } int start = prev + 1; int end = n - 1; if (start <= end) { longlong length = end - start + 1; sum_not += length * (length + 1) / 2; }
longlong contribution = total_arrays - sum_not; result += contribution; } cout << result << endl; }
constint MAXN = 2005; int sum[MAXN][MAXN]; int h[MAXN][MAXN], h_sum[MAXN][MAXN]; int v[MAXN][MAXN], v_sum[MAXN][MAXN]; char grid[MAXN][MAXN]; voidsolve() { int n, m, q; scanf("%d%d%d", &n, &m, &q); for (int i = 1; i <= n; ++i) { scanf("%s", grid[i] + 1); }
// Precompute sum array for 1's for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + (grid[i][j] == '1' ? 1 : 0); } }
// Precompute h and v arrays for edges for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { // Horizontal edges (right) if (j < m && grid[i][j] == '1' && grid[i][j+1] == '1') { h[i][j] = 1; } else { h[i][j] = 0; } // Vertical edges (down) if (i < n && grid[i][j] == '1' && grid[i+1][j] == '1') { v[i][j] = 1; } else { v[i][j] = 0; } } }
// Precompute prefix sums for h and v for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { h_sum[i][j] = h_sum[i-1][j] + h_sum[i][j-1] - h_sum[i-1][j-1] + h[i][j]; v_sum[i][j] = v_sum[i-1][j] + v_sum[i][j-1] - v_sum[i-1][j-1] + v[i][j]; } }
while (q--) { int x1, y1, x2, y2; scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
int S = sum[x2][y2] - sum[x1-1][y2] - sum[x2][y1-1] + sum[x1-1][y1-1];
int H = 0; int j1 = y1, j2 = y2 - 1; if (j1 <= j2 && x1 <= x2) { H = h_sum[x2][j2] - h_sum[x1-1][j2] - h_sum[x2][j1-1] + h_sum[x1-1][j1-1]; }
int V = 0; int i1 = x1, i2 = x2 - 1; if (i1 <= i2 && y1 <= y2) { V = v_sum[i2][y2] - v_sum[i1-1][y2] - v_sum[i2][y1-1] + v_sum[i1-1][y1-1]; }