From 72952d982b34d587603273bc315e4959806d0cd4 Mon Sep 17 00:00:00 2001 From: Zengtudor Date: Tue, 15 Jul 2025 10:00:16 +0800 Subject: [PATCH] update --- src/7/15/U76012.cpp | 81 +++++++++++++++++-------- src/7/15/U76012.md | 145 ++++++++++++++++++++++++++++++++++++++++++++ src/test.cpp | 57 ++--------------- 3 files changed, 205 insertions(+), 78 deletions(-) create mode 100644 src/7/15/U76012.md diff --git a/src/7/15/U76012.cpp b/src/7/15/U76012.cpp index 6e111ce..a1b0f86 100644 --- a/src/7/15/U76012.cpp +++ b/src/7/15/U76012.cpp @@ -1,38 +1,69 @@ -#include -#include #include #include #include +#include +using namespace std; -using ll = int64_t; -const ll maxn = 100+5; -ll n,p,ans{}; -std::vector a; +#define NV(v){cout<<#v<<" : "<<(v)<<'\n';} -void cycle(ll bit){ //should be 128bit - ll sum{}; - for(ll i=1;i<=n;i++){ - if((1<<(i-1))&bit){ - sum=(sum+a[i])%p; +vector gen(vector& nums, long long p) { + vector res; + res.push_back(0); + for (int num : nums) { + int sz = res.size(); + for (int i = 0; i < sz; i++) { + long long new_sum = (res[i] + num) % p; + res.push_back(new_sum); } } - ans=std::max(ans,sum); + return res; } -int main(){ - a.resize(maxn); - - std::iostream::sync_with_stdio(false),std::cin.tie(nullptr),std::cout.tie(nullptr); - std::cin>>n>>p; - - for (ll i=1;i<=n;i++) { - std::cin>>a[i]; - a[i]%=p; +int main() { + iostream::sync_with_stdio(false); + cin.tie(nullptr); + cout.tie(nullptr); + int n; + long long p; + cin >> n >> p; + vector a(n); + for (int i = 0; i < n; i++) { + cin >> a[i]; } - for(ll i=1;i<(1< A(a.begin(), a.begin() + mid); + vector B(a.begin() + mid, a.end()); + + vector T1 = gen(A, p); + vector T2 = gen(B, p); + + sort(T2.begin(), T2.end()); + auto last = unique(T2.begin(), T2.end()); + T2.erase(last, T2.end()); + + long long ans = 0; + for (long long x : T1) { + long long target = p - 1 - x; + auto it = upper_bound(T2.begin(), T2.end(), target); + if (it != T2.begin()) { + --it; + ans = max(ans, x + *it); + } + + if (!T2.empty()) { + long long max_val = T2.back(); + if (max_val >= p - x) { + ans = max(ans, (x + max_val) % p); + } + } + } + + cout << ans << endl; + return 0; } \ No newline at end of file diff --git a/src/7/15/U76012.md b/src/7/15/U76012.md new file mode 100644 index 0000000..7ced4af --- /dev/null +++ b/src/7/15/U76012.md @@ -0,0 +1,145 @@ +为了解决这个问题,我们需要从给定的物品中选择一个子集,使得子集的价值之和在模 \( p \) 意义下最大。由于 \( n \) 的最大值为 36,直接枚举所有子集(\( 2^{36} \) 种可能)是不可行的。因此,我们采用分治策略(Meet-in-the-Middle)来高效地解决问题。 + +### 方法思路 +1. **问题分析**:我们需要在 \( n \) 个物品中选出一个子集,使得子集和模 \( p \) 的值最大。由于 \( n \) 最大为 36,直接枚举所有子集不可行,因此采用分治策略将问题分成两半处理。 +2. **分治策略**:将物品分成两部分 \( A \) 和 \( B \),分别计算两部分的所有子集和模 \( p \) 的结果,得到两个列表 \( T1 \) 和 \( T2 \)。 +3. **合并结果**:对于 \( T1 \) 中的每个值 \( x \),在 \( T2 \) 中寻找一个值 \( y \) 使得 \( (x + y) \mod p \) 最大。具体分为两种情况: + - **情况1**:找到 \( T2 \) 中小于等于 \( p - 1 - x \) 的最大值 \( y \),此时 \( x + y \) 即为候选值。 + - **情况2**:如果 \( T2 \) 的最大值大于等于 \( p - x \),则计算 \( (x + \text{max\_val}) \mod p \) 作为候选值。 +4. **优化处理**:对 \( T2 \) 排序并去重,以便使用二分查找高效地处理情况1。 + +### 解决代码 +```cpp +#include +#include +#include +using namespace std; + +vector generate(vector& nums, long long p) { + vector res; + res.push_back(0); + for (int num : nums) { + int sz = res.size(); + for (int i = 0; i < sz; i++) { + long long new_sum = (res[i] + num) % p; + res.push_back(new_sum); + } + } + return res; +} + +int main() { + int n; + long long p; + cin >> n >> p; + vector a(n); + for (int i = 0; i < n; i++) { + cin >> a[i]; + } + + if (n == 0) { + cout << 0 << endl; + return 0; + } + + int mid = n / 2; + vector A(a.begin(), a.begin() + mid); + vector B(a.begin() + mid, a.end()); + + vector T1 = generate(A, p); + vector T2 = generate(B, p); + + sort(T2.begin(), T2.end()); + auto last = unique(T2.begin(), T2.end()); + T2.erase(last, T2.end()); + + long long ans = 0; + for (long long x : T1) { + long long target = p - 1 - x; + auto it = upper_bound(T2.begin(), T2.end(), target); + if (it != T2.begin()) { + --it; + ans = max(ans, x + *it); + } + + if (!T2.empty()) { + long long max_val = T2.back(); + if (max_val >= p - x) { + ans = max(ans, (x + max_val) % p); + } + } + } + + cout << ans << endl; + return 0; +} +``` + +### 代码解释 +1. **generate函数**:计算给定物品列表的所有子集和模 \( p \) 的结果。使用迭代方法生成所有子集和,并在每一步进行模 \( p \) 操作以避免溢出。 +2. **主函数**: + - **输入处理**:读取物品数量 \( n \)、模数 \( p \) 和物品价值列表 \( a \)。 + - **分治处理**:将物品列表分成两部分 \( A \) 和 \( B \)。 + - **子集和生成**:分别生成 \( A \) 和 \( B \) 的子集和模 \( p \) 列表 \( T1 \) 和 \( T2 \)。 + - **合并优化**:对 \( T2 \) 排序并去重,便于二分查找。 + - **候选值计算**:对于 \( T1 \) 中的每个值 \( x \): + - 在 \( T2 \) 中查找小于等于 \( p - 1 - x \) 的最大值,计算候选值 \( x + y \)。 + - 检查 \( T2 \) 的最大值是否大于等于 \( p - x \),若是,则计算候选值 \( (x + \text{max\_val}) \mod p \)。 + - **输出结果**:输出所有候选值中的最大值。 + +通过分治策略和高效合并,该方法能够在合理的时间内解决 \( n \) 较大的问题。 + +`generate` 函数是解决本题的核心函数之一,其原理是**动态生成所有可能的子集和(模 p 的结果)**。以下是详细解释: + +### 工作原理 +1. **初始化**: + 创建一个结果向量 `res`,初始只包含 `0`(表示空集的子集和为 0)。 + +2. **迭代处理每个物品**: + 对于输入列表 `nums` 中的每个物品 `num`: + - 遍历当前 `res` 中已有的所有子集和 + - 对每个已有子集和 `res[i]`,计算新子集和: + `new_sum = (res[i] + num) % p` + - 将新子集和加入结果集 `res` + +3. **动态扩展**: + 每次处理一个新物品时,`res` 的大小会翻倍(因为每个已有子集可以选择包含或不包含当前物品)。最终 `res` 包含所有可能的子集和(模 p 结果)。 + +### 示例演示 +假设输入 `nums = [2, 3]`, `p = 5`: +1. 初始状态:`res = [0]` +2. 处理物品 `2`: + - 基于 `0` 生成新和:`(0+2)%5=2` + - 更新 `res = [0, 2]` +3. 处理物品 `3`: + - 基于 `0` 生成:`(0+3)%5=3` + - 基于 `2` 生成:`(2+3)%5=0` + - 更新 `res = [0, 2, 3, 0]` → 去重后为 `[0, 2, 3]` + +### 关键特性 +1. **模运算优化**: + 每次计算新和时立即取模 `%p`,防止数值溢出并缩小状态空间。 + +2. **指数级扩展**: + 若有 k 个物品,会生成最多 `2^k` 个子集和。因此适用于 `k ≤ 18` 的情况(本题中 n≤36 分治后每半最多18个物品)。 + +3. **保留所有可能性**: + 最终 `res` 包含所有可能的子集和(模 p),如示例中: + - 空集:0 + - 单物品:2, 3 + - 两物品:5≡0 (mod 5) + +### 在本题中的应用 +分治策略中的使用: +```cpp +vector A = 前半物品 → 生成 T1 +vector B = 后半物品 → 生成 T2 +``` +- 分别生成两个较小的子集和集合 +- 合并时只需 `O(|T1|*log|T2|)` 时间而非 `O(2^n)` + +### 复杂度分析 +- 时间:`O(2^k)`(k 是输入列表长度) +- 空间:`O(2^k)` 存储子集和 + +> 这种方法是分治策略(Meet-in-the-Middle)的关键前置步骤,通过将指数级问题拆分为两个较小的指数问题(`O(2^{n/2})`),显著降低整体复杂度。 \ No newline at end of file diff --git a/src/test.cpp b/src/test.cpp index 7925ad2..c895cab 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -1,55 +1,6 @@ -#include -#include #include -using namespace std; - -struct TreeNode { - vector children; -}; - -vector tree; -vector enjoyment; -vector> dp; - -void dfs(int u, int parent) { - dp[u][0] = 0; - dp[u][1] = enjoyment[u]; - - for (int v : tree[u].children) { - if (v == parent) continue; - dfs(v, u); - dp[u][0] += max(dp[v][0], dp[v][1]); - dp[u][1] += dp[v][0]; - } - - for (int v : tree[u].children) { - if (v == parent) continue; - // long long option = dp[u][1] - dp[v][0] + dp[v][1] - b; - // dp[u][1] = max(dp[u][1], option); - } -} - -int main() { - int n, b; - cin >> n >> b; - enjoyment.resize(n + 1); - tree.resize(n + 1); - dp.resize(n + 1, vector(2)); - - for (int i = 1; i <= n; ++i) { - cin >> enjoyment[i]; - } - - for (int i = 0; i < n - 1; ++i) { - int x, y; - cin >> x >> y; - tree[x].children.push_back(y); - tree[y].children.push_back(x); - } - - dfs(1, -1); - long long result = max(0LL, max(dp[1][0], dp[1][1])); - cout << result << endl; - - return 0; +#include +int main(){ + int a[] = {0,1,1,}; + std::cout<<(std::unique(a+1,a+1+2))-(a+1)<<'\n'; } \ No newline at end of file