[题解]AT_abc236_f [ABC236F] Spices
思路
首先对所有的 从小到大排序,然后对于每一个值如果之前能凑出就不选,否则就选。
这样做显然是对的。令 表示将 排序之后,对应原来的下标; 表示选出数的集合; 表示最终选出数的集合。可以证明两个问题:
- 如果 可以被已选出数凑出,则不需要选 。
- 如果 不可以被已选出的数凑出,则选 最优。
对于第一个问题,我们总可以选出 ,使得 。
如果 ,并且能选出 ,使得 ,那么一定有 。
所以选定 不是最优的方式。
对于第二个问题,显然会存在 ,并且有 ,使得 ,即 ,即 。又因为 不能被凑出,所以 中一定有一个元素不在 中。
那么,对于所有的 都存在 ,使得 ,这里假令 。那么有:
所以即使 中没有 ,但加上 依旧能使得条件成立。
又因为此时 中没有 ,所以 ,因此选 更优。
但是这个复杂度看似是 的,其中 。但是其实是 的。
不难发现最多选出 个数就能将 中的所有数凑齐。
Code
#include <bits/stdc++.h>
#define re register
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n,m,ans;
bool vis[N];
struct point{
int x,id;
friend bool operator <(const point &a,const point &b){
return a.x < b.x;
}
}arr[N];
inline int read(){
int r = 0,w = 1;
char c = getchar();
while (c < '0' || c > '9'){
if (c == '-') w = -1;
c = getchar();
}
while (c >= '0' && c <= '9'){
r = (r << 3) + (r << 1) + (c ^ 48);
c = getchar();
}
return r * w;
}
signed main(){
n = read();
m = (1ll << n) - 1;
for (re int i = 1;i <= m;i++){
arr[i].x = read();
arr[i].id = i;
}
sort(arr + 1,arr + m + 1);
for (re int i = 1;i <= m;i++){
if (vis[arr[i].id]) continue;
ans += arr[i].x;
vis[arr[i].id] = true;
for (re int j = 1;j <= m;j++) vis[j ^ arr[i].id] |= vis[j];
}
printf("%lld",ans);
return 0;
}
作者:WaterSun
出处:https://www.cnblogs.com/WaterSun/p/18261966
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】