1 条题解
-
0
题意理解 我们有 个不重叠的区间 ,每个区间内的整数代表化学品的标号。
规则:两种化学品 和 可以混合当且仅当 。
要求:从所有化学品中选出 三种不同的化学品,使得它们两两可以混合(即形成一个合法三元组),求三元组数量,对 取模。
关键观察
- 异或性质 条件 不是传递的,但题目要求的是三元组 两两满足 , , 。
一个重要的性质:如果 且 ,那么 吗? 不一定,但我们可以利用 异或的三角不等式: 不直接成立,但这里需要更精细的分析。
实际上,一个更好的思路是:考虑 按异或的某些高位进行分组。
- 高位分组思想 设我们在二进制下从高到低考虑。假设当前考虑第 位。
如果 的第 位是 : 那么对于两个数 ,如果它们在第 位相同,则 的第 位是 ,一定 (因为 这一位是 1,更高位相同情况下, 成立)。 如果它们在第 位不同,则 的第 位是 ,需要看更低位是否 的低位。
如果 的第 位是 : 那么 的第 位必须是 才可能 ,即 和 在第 位必须相同。
- 问题转化 我们可以将所有数按照它们与 的二进制前缀关系分组,使得组内任意两个数异或 ,而不同组的数异或可能 。
更具体地,在 01-Trie 上,从高到低匹配 的位:
如果 的当前位是 ,那么当前节点的两个子树内的数可以互相配对(因为异或后这一位是 1,等于 的这一位,需要继续检查低位),但同一子树内的数异或后这一位是 0,一定 。
如果 的当前位是 ,那么只能在同一子树内配对(因为跨子树异或后这一位是 1 > 0)。
解法思路
- Trie 树划分 构建深度为 31 的 01-Trie(因为值域 在 内)。
在 Trie 上 DFS,同时携带当前路径与 的前缀比较信息。
对于每个节点,维护区间内数的个数。
- 统计三元组 对于每个节点,根据 的当前位决定如何统计:
Case 1: 的当前位 = 1
三元组全部来自左子树:
三元组全部来自右子树:
两个来自左,一个来自右:需要这两个左子树数与那个右子树数两两异或 。 由于左子树内任意两个数异或这一位是 0,一定 ;左与右的异或这一位是 1,需要低位 的低位,这变成子问题。
两个来自右,一个来自左:对称情况。
Case 2: 的当前位 = 0
只能全部来自同一子树(左或右),递归处理。
- 区间处理 由于输入是区间而不是单个点,我们需要高效计算区间在 Trie 节点上的分布。
可以利用 数位 DP 的技巧: 定义函数 表示区间 在 Trie 某个子树下的数的个数,以及更复杂的统计信息。
算法框架 cpp #include <bits/stdc++.h> using namespace std; using ll = long long;
const int MOD = 1e9 + 7;
ll mod_add(ll a, ll b) { return (a + b) % MOD; } ll mod_mul(ll a, ll b) { return a * b % MOD; }
ll C2(ll n) { // 组合数 C(n,2) if (n < 2) return 0; return n * (n - 1) / 2 % MOD; }
ll C3(ll n) { // 组合数 C(n,3) if (n < 3) return 0; return n * (n - 1) / 2 % MOD * (n - 2) / 3 % MOD; }
// 计算区间 [l, r] 在二进制位 d 以下的统计信息 struct Info { ll cnt; // 数的个数 ll sum; // 数的和(可能需要) // 可以扩展更多信息 };
class Cowmistry { private: int K; vector<pair<ll, ll>> intervals;
// 计算区间 [l, r] 与 Trie 节点 [L, R] 的交集大小 ll count_intersect(ll l, ll r, ll L, ll R) { if (r < L || l > R) return 0; return min(r, R) - max(l, L) + 1; } // 主递归函数:在当前二进制位 d,当前节点覆盖 [L, R],统计三元组数量 ll solve(int d, ll L, ll R, ll l1, ll r1, ll l2, ll r2, ll l3, ll r3) { // 简化:这里应实现复杂的区间交集统计 // 实际代码需要处理多个区间与当前节点的交集 return 0; }
public: Cowmistry(int k, vector<pair<ll, ll>>& inters) : K(k), intervals(inters) {}
ll compute() { ll ans = 0; // 实际实现:从高位到低位递归 // 这里给出核心逻辑框架 // 对每个区间单独计算内部的三元组 for (auto& p : intervals) { ll l = p.first, r = p.second; // 内部三元组数量 = C(r-l+1, 3) 如果区间长度>=3 if (r - l + 1 >= 3) { ans = (ans + C3(r - l + 1)) % MOD; } } // 还需要计算跨区间的三元组 // 这里需要实现上述 Trie 方法 return ans; }
};
int main() { ios::sync_with_stdio(false); cin.tie(nullptr);
int N, K; cin >> N >> K; vector<pair<ll, ll>> intervals(N); for (int i = 0; i < N; i++) { cin >> intervals[i].first >> intervals[i].second; } Cowmistry solver(K, intervals); cout << solver.compute() << endl; return 0;
} 优化与实现细节 记忆化搜索:在 Trie 遍历时,对相同形态的节点复用计算结果。
区间处理:由于区间不重叠且有序,可以高效计算区间与 Trie 节点的交集。
数学简化:对于 的特殊情况,所有数只要在某个大小不超过 的块内,就两两兼容,可以直接用组合数计算。
复杂度分析 时间复杂度:,其中
空间复杂度: 用于递归栈
总结 这道题的核心在于 利用 01-Trie 按位分组,根据 的二进制位决定哪些数可以组成合法三元组。通过递归处理高位到低位,将问题分解为子问题,最终合并得到答案。
由于实现较为复杂,需要仔细处理区间计数和组合数学部分,但思路清晰后可以系统性地解决。
- 1
信息
- ID
- 3499
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 6
- 已通过
- 1
- 上传者