This commit is contained in:
Zengtudor 2025-07-15 10:00:16 +08:00
parent c49ccfe4fd
commit 72952d982b
3 changed files with 205 additions and 78 deletions

View File

@ -1,38 +1,69 @@
#include <algorithm>
#include <cstdint>
#include <iostream>
#include <istream>
#include <vector>
#include <algorithm>
using namespace std;
using ll = int64_t;
const ll maxn = 100+5;
ll n,p,ans{};
std::vector<ll> 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<long long> gen(vector<int>& nums, long long p) {
vector<long long> 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<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for(ll i=1;i<(1<<n);i++){
cycle(i);
}
if (n == 0) {
cout << 0 << endl;
return 0;
}
std::cout<<ans<<'\n';
int mid = n / 2;
vector<int> A(a.begin(), a.begin() + mid);
vector<int> B(a.begin() + mid, a.end());
vector<long long> T1 = gen(A, p);
vector<long long> 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;
}

145
src/7/15/U76012.md Normal file
View File

@ -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 <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<long long> generate(vector<int>& nums, long long p) {
vector<long long> 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<int> 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<int> A(a.begin(), a.begin() + mid);
vector<int> B(a.begin() + mid, a.end());
vector<long long> T1 = generate(A, p);
vector<long long> 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<int> A = 前半物品 → 生成 T1
vector<int> B = 后半物品 → 生成 T2
```
- 分别生成两个较小的子集和集合
- 合并时只需 `O(|T1|*log|T2|)` 时间而非 `O(2^n)`
### 复杂度分析
- 时间:`O(2^k)`k 是输入列表长度)
- 空间:`O(2^k)` 存储子集和
> 这种方法是分治策略Meet-in-the-Middle的关键前置步骤通过将指数级问题拆分为两个较小的指数问题`O(2^{n/2})`),显著降低整体复杂度。

View File

@ -1,55 +1,6 @@
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct TreeNode {
vector<int> children;
};
vector<TreeNode> tree;
vector<int> enjoyment;
vector<vector<long long>> 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<long long>(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 <iostream>
int main(){
int a[] = {0,1,1,};
std::cout<<(std::unique(a+1,a+1+2))-(a+1)<<'\n';
}