整齐摆放
n个长方形,第i个长方形的两条边长len1[i]、len2[i]已知。另外我们有一个只包含第一象限的平面直角坐标系。
现在我们将这n个长方形依次摆放在坐标系的x轴上,不允许重叠,且每个长方形放置后在y轴上的高度不超过m。
题目保证max(min(len1, len2))<=m
求摆放完成所有长方形,至少需要占用x轴的多少长度?
C++
#include <bits/stdc++.h>
using namespace std;
int main() {
int n; // n 个长方形
int64_t m; // 每个长方形放置后的高度不超过m
std::cin >> n >> m;
// 读取每个长方形的边长
std::vector<int64_t> len1(n);
std::vector<int64_t> len2(n);
for (int i = 0; i < n; ++i) {
std::cin >> len1[i] >> len2[i];
}
// 将n个长方形放置到x轴上
// 最少需要占用的x轴的长度
int64_t ans = 0;
for (int i = 0; i < n; ++i) {
// 先看max_len是否超出m,如果不超出用min_len
// 如果max_len超出m,则只能用max_len
int64_t max_len = std::max(len1[i], len2[i]);
int64_t min_len = std::min(len1[i], len2[i]);
if (max_len <= m) {
ans += min_len;
} else {
ans += max_len;
}
}
std::cout << ans;
}
注意题目中的数据范围已经达到2^9这个数量级,因此需要使用int64_t。
美丽数组
我们有一个感兴趣的数x,已知0<=x<2^20。
现在我们寻找一个美丽数组。如果一个数组a满足以下条件,则我们称它为美丽数组:
- 数组a中的每个元素a[i],都有a[i]<2^20
- 数组a中不包含重复的元素
- 额外地,如果数组a的长度大于1,我们还要求:对于数组a中任意两个元素a[i]和a[j](a[i]≠a[j]),都有
a[i]&a[j]==x
(其中&指的是按位与运算)。
现在给定一个已知的x,请你求出满足上述条件的一个最长的美丽数组,输出它的长度和各个数组元素。
如果存在多个最长的美丽数组,输出任意一个即可
测试用例
输入
2
1048575
1048573
输出
1
1048575
2
1048573 1048575
题解
首先通过理解题意,明确这是一道构造题。
既然题目要求对美丽数组中的任意元素a[i]和a[j],都有a[i]&a[j]==x
,那么一定有以下推论:
- x上的某一位为1,则a[i]和a[j]中该位一定为均为1
- x上的某一位为0,则a[i]和a[j]至少有一者,该位为0
我们先来看推论1。它的一个更弱的表述是,对于美丽数组中的任意一个元素a[k],如果x上的某一位为1,则a[k]中该位一定为均为1。即a[k]中至少包含了x中值为1的二进制位。
至于推论2,又该如何保证呢?
首先,对于构造出来数组元素a[k],我们当然可以直接躺平,不在x的基础上设置任何额外的值为1的二进制位。但这样美丽数组中就始终只有x一个元素了,不符合题目中最长的要求。
因此我们可以再往前走一步,对于每个x上为0的二进制位,我们只让美丽数组中一个元素在该位上为1,而其他元素中该位均为0。这样既可以保证我们构造出来的美丽数组符合推论2,又可以让它的长度达到最长。
C++
#include <iostream>
#include <vector>
int main() {
int t;
std::cin >> t;
while (t-- > 0) {
int x;
std::cin >> x;
std::vector<int> ans;
ans.push_back(x);
int peek = 1;
for (int i = 0; i < 20; ++i) {
if ((peek & x) == 0) {
ans.push_back(x + peek);
}
peek <<= 1;
}
std::cout << ans.size() << std::endl;
for (int i = 0; i < ans.size(); ++i) {
std::cout << ans[i];
if (i != ans.size() - 1) std::cout << " ";
}
std::cout << std::endl;
}
}
带权距离
给定一棵以节点1为根的树,树上共有n个节点,其中某些节点被标记为"红色"。树上的每条无向边具有一个正整数权重w[u][v]。
现在在这棵树上执行q次操作<t, v>。每次操作有两种类型:
- 若t=1,表示刷新节点v的颜色状态。如原先v的颜色为非红,则变为红色;反之亦然。该操作不会打印任何内容。
- 若t-2,表示查询并打印节点v到所有红色节点的带权距离之和。
带权距离指的是路径上所有边的权重的总和
输入描述
第一行输入n、q。其中1<=n, q<=2*10^5。
第二行输入n个整数c[1]、c[2]、...、c[n]。其中c[i]∈{0, 1}。当c[i]=1,表示节点i为红色,否则则为非红。
接下来n-1行,每行输入三个整数u、v、w,表示树上的一条带权重无向边。
最后q行,每行输入一个t和v(t∈{1, 2},1<=v<=n),表示一次操作。
暴力解法
以下解法会导致空间超出限制:
C++
#include <bits/stdc++.h>
using namespace std;
// state[i] 节点i是不是红色的
std::vector<bool> state;
std::vector<std::vector<int>> graph;
std::unordered_set<int> vis;
int ComputePathSum(int n, const int curr, int path) {
int sum = 0;
for (int next = 0; next < n; ++next) {
if (graph[curr][next] && vis.count(next) == 0) {
if (state[next]) {
sum += path + graph[curr][next];
}
vis.insert(next);
sum += ComputePathSum(n, next, path + graph[curr][next]);
}
}
return sum;
}
int main() {
int n; // 节点数
int q; // 操作数
std::cin >> n >> q;
state.assign(n, false);
for (int i = 0; i < n; ++i) {
int t;
std::cin >> t;
state[i] = t;
}
graph.assign(n, std::vector<int>(n, 0));
for (int i = 0; i < n - 1; ++i) {
int u, v, w;
std::cin >> u >> v >> w;
graph[u - 1][v - 1] = w;
graph[v - 1][u - 1] = w;
}
for (int i = 0; i < q; ++i) {
int t, v;
std::cin >> t >> v;
if (t == 1) {
state[v - 1] = !state[v - 1];
} else if (t == 2) {
// 查询节点v到所有红色节点的带权距离和
vis.clear();
vis.insert(v - 1);
std::cout << ComputePathSum(n, v - 1, 0) << std::endl;
}
}
}
以下解法会导致时间超出限制:
C++
#include <bits/stdc++.h>
using namespace std;
// state[i] 节点i是不是红色的
std::vector<bool> state;
std::vector<std::vector<std::pair<int, int>>> graph;
std::unordered_set<int> vis;
int ComputePathSum(int n, const int curr, int path) {
int sum = 0;
for (auto [next, w] : graph[curr]) {
if (vis.count(next) == 0) {
if (state[next]) {
sum += path + w;
}
vis.insert(next);
sum += ComputePathSum(n, next, path + w);
}
}
return sum;
}
int main() {
int n; // 节点数
int q; // 操作数
std::cin >> n >> q;
state.assign(n, false);
for (int i = 0; i < n; ++i) {
int t;
std::cin >> t;
state[i] = t;
}
graph.resize(n);
for (int i = 0; i < n - 1; ++i) {
int u, v, w;
std::cin >> u >> v >> w;
graph[u - 1].push_back({v - 1, w});
graph[v - 1].push_back({u - 1, w});
}
for (int i = 0; i < q; ++i) {
int t, v;
std::cin >> t >> v;
if (t == 1) {
state[v - 1] = !state[v - 1];
} else if (t == 2) {
// 查询节点v到所有红色节点的带权距离和
vis.clear();
vis.insert(v - 1);
std::cout << ComputePathSum(n, v - 1, 0) << std::endl;
}
}
}
最优解法
使用树链剖分 + 线段树,以下是ai提供的代码...
C++
#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
const int MAXN = 200005;
using ll = long long;
// Graph representation
vector<pair<int, int>> adj[MAXN];
bool is_red[MAXN];
// HLD variables
int parent[MAXN];
ll depth[MAXN]; // Weighted depth from root
int subtree_size[MAXN];
int heavy_child[MAXN];
int chain_head[MAXN];
int pos[MAXN]; // Position in the segment tree array
ll edge_weight_val[MAXN]; // edge_weight_val[u] is weight of edge (u, parent[u])
int timer;
// Global sums for the main formula
ll total_red_count = 0;
ll total_red_dist_sum = 0;
// Segment Tree
struct Node {
ll sum_w; // Sum of edge weights in the range
ll sum_w_sz; // Sum of (weight * sz) in the range
ll lazy; // Lazy tag for updates
} tree[4 * MAXN];
// DFS to calculate depths, parents, subtree sizes, and heavy children
void dfs1(int u, int p, ll d) {
parent[u] = p;
depth[u] = d;
subtree_size[u] = 1;
heavy_child[u] = 0;
int max_sz = 0;
for (auto& edge : adj[u]) {
int v = edge.first;
int w = edge.second;
if (v == p) continue;
edge_weight_val[v] = w;
dfs1(v, u, d + w);
subtree_size[u] += subtree_size[v];
if (subtree_size[v] > max_sz) {
max_sz = subtree_size[v];
heavy_child[u] = v;
}
}
}
// DFS to perform decomposition and assign positions for the segment tree
void dfs2(int u, int head) {
chain_head[u] = head;
pos[u] = ++timer;
if (heavy_child[u]) {
dfs2(heavy_child[u], head);
}
for (auto& edge : adj[u]) {
int v = edge.first;
if (v != parent[u] && v != heavy_child[u]) {
dfs2(v, v);
}
}
}
// --- Segment Tree Functions ---
void push_up(int p) {
tree[p].sum_w = tree[2 * p].sum_w + tree[2 * p + 1].sum_w;
tree[p].sum_w_sz = tree[2 * p].sum_w_sz + tree[2 * p + 1].sum_w_sz;
}
void push_down(int p, int L, int R) {
if (tree[p].lazy == 0) return;
int mid = L + (R - L) / 2;
// Apply lazy tag to left child
tree[2 * p].sum_w_sz += tree[p].lazy * tree[2 * p].sum_w;
tree[2 * p].lazy += tree[p].lazy;
// Apply lazy tag to right child
tree[2 * p + 1].sum_w_sz += tree[p].lazy * tree[2 * p + 1].sum_w;
tree[2 * p + 1].lazy += tree[p].lazy;
tree[p].lazy = 0;
}
void build(int p, int L, int R, const vector<ll>& base_weights) {
tree[p].lazy = 0;
if (L == R) {
tree[p].sum_w = base_weights[L];
tree[p].sum_w_sz = 0; // sz is initially 0 for all nodes
return;
}
int mid = L + (R - L) / 2;
build(2 * p, L, mid, base_weights);
build(2 * p + 1, mid + 1, R, base_weights);
push_up(p);
}
void update(int p, int L, int R, int update_L, int update_R, int delta) {
if (update_L <= L && R <= update_R) {
tree[p].sum_w_sz += (ll)delta * tree[p].sum_w;
tree[p].lazy += delta;
return;
}
push_down(p, L, R);
int mid = L + (R - L) / 2;
if (update_L <= mid) {
update(2 * p, L, mid, update_L, update_R, delta);
}
if (update_R > mid) {
update(2 * p + 1, mid + 1, R, update_L, update_R, delta);
}
push_up(p);
}
ll query(int p, int L, int R, int query_L, int query_R) {
if (query_L <= L && R <= query_R) {
return tree[p].sum_w_sz;
}
push_down(p, L, R);
int mid = L + (R - L) / 2;
ll sum = 0;
if (query_L <= mid) {
sum += query(2 * p, L, mid, query_L, query_R);
}
if (query_R > mid) {
sum += query(2 * p + 1, mid + 1, R, query_L, query_R);
}
return sum;
}
// --- HLD Path Functions ---
void path_update(int u, int n, int delta) {
while (u) {
update(1, 1, n, pos[chain_head[u]], pos[u], delta);
u = parent[chain_head[u]];
}
}
ll path_query(int u, int n) {
ll sum = 0;
while (u) {
sum += query(1, 1, n, pos[chain_head[u]], pos[u]);
u = parent[chain_head[u]];
}
return sum;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n, q;
cin >> n >> q;
for (int i = 1; i <= n; ++i) {
cin >> is_red[i];
}
for (int i = 0; i < n - 1; ++i) {
int u, v, w;
cin >> u >> v >> w;
adj[u].push_back({ v, w });
adj[v].push_back({ u, w });
}
// 1. Preprocessing for HLD
dfs1(1, 0, 0);
timer = 0;
dfs2(1, 1);
// 2. Build Segment Tree
vector<ll> base_weights(n + 1);
for (int i = 1; i <= n; ++i) {
base_weights[pos[i]] = edge_weight_val[i];
}
build(1, 1, n, base_weights);
// 3. Process initial red nodes
for (int i = 1; i <= n; ++i) {
if (is_red[i]) {
total_red_count++;
total_red_dist_sum += depth[i];
path_update(i, n, 1);
}
}
// 4. Handle queries
for (int i = 0; i < q; ++i) {
int type, v;
cin >> type >> v;
if (type == 1) {
int delta = is_red[v] ? -1 : 1;
is_red[v] = !is_red[v];
total_red_count += delta;
total_red_dist_sum += (ll)delta * depth[v];
path_update(v, n, delta);
}
else {
ll f_v = path_query(v, n);
ll result = total_red_count * depth[v] + total_red_dist_sum - 2 * f_v;
cout << result << "\n";
}
}
return 0;
}