当前位置: 首页 > news >正文

P13552 鱼类考古学

来水题解了喵。这题有紫?

首先合理猜测最终的操作一定是先 \(n-k\)\(1\) 操作后若干次 \(2\) 操作,不妨认为这是对的考虑怎么证明一下。转化一下,我们要证的就是:\((x \otimes y) + z \geq (x + y) \otimes z\)(话说为什么按位与符号是这个,好奇怪喵)。容易发现 \((x + y) \otimes z \leq z\),而 \((x \otimes y) + z \geq z\)

接着我们考虑贪心的进行前 \(n-k\)\(1\) 操作。我们发现 \(a \otimes b = a + b - a | b\) 其中 \(|\) 为按位或操作。假设我们每步操作的两个数的按位或之和为 \(b\),那么我们最终的答案即为 \(\sum a_i - b\),也就是说我们要最小化 \(b\)

于是我们发现:对于二进制下从低到高第 \(i\) 位为 \(0\) 的数和第 \(i\) 位为 \(1\) 的数(不妨假设更高位均相同),优先选择第 \(i\) 位为 \(0\) 的数进行操作是更优的(因为前者操作后的产生的按位或第 \(i\) 位为 \(0\) 而后者为 \(1\))。于是我们有了一个贪心策略:从大到小枚举 \(2^i\),优先将这一位为 \(0\) 的数先操作。

考虑如何维护这个东西。很显然的想到 01-Trie,建出树后从根开始向下遍历保证枚举的 \(2^i\) 从大到小。若这一位为 \(0\) 的数足够 \(k\) 次操作,就向 \(0\) 儿子继续遍历;否则需要对 \(1\) 儿子进行操作。设 \(0\) 儿子子树中所有数的按位与结果为 \(x\),那么我们要将这个数添加到我们进行操作的序列中去。如果每次记录这样的 \(x\) 未免太过麻烦,我们令 \(x\) 的第 \(i\) 位为 \(1\) 将其加入 Trie 树中,这样就可以优雅的继续向 \(1\) 儿子递归,统计答案时减去 \(2^i\) 即可。

考虑如何获得答案。在递归过程中,如果递归到叶子节点且还剩余 \(k\) 次操作与 \(m\) 个数,那么答案就加上 \((m - k) \times val\)\(val\) 为当前遍历到的数);若向 \(0\) 儿子遍历,那么就可以取遍 \(1\) 儿子子树中的和加入答案。可以感性理解一下这为什么是对的。

回顾整道题,我们只需要在 Trie 树上维护子树中数的个数、子树中数的和与子树中数的按位与即可,代码很简单。

const int N=1e6+5,S=2e7+5;
int T,n,k,a[N];
int tr[S][2],siz[S],nd[S],cnt;
ll ans,sum[S];inline int new_node() {++cnt,siz[cnt]=0,nd[cnt]=(1<<30)-1;sum[cnt]=tr[cnt][0]=tr[cnt][1]=0;return cnt;
}
inline void insert(int x) {int p=0; siz[0]++;for(int j=29;j>=0;j--) {int dir=(x>>j)&1;if(!tr[p][dir]) tr[p][dir]=new_node();siz[p=tr[p][dir]]++,nd[p]&=x,sum[p]+=x;}return ;
}
#define lc tr[p][0]
#define rc tr[p][1]
inline void solve(int p,int k,int bit,int num) {// printf("solve(%d,%d,%d,%d): %lld\n",p,k,bit,num,ans);if(!bit) {ans+=(ll)num*(siz[p]-k);// printf("%d %d %d %lld\n",num,siz[p],k,ans);return ;}if(!lc) return solve(rc,k,bit-1,num|(1<<bit-1));if(!rc) return solve(lc,k,bit-1,num);if(lc&&siz[lc]>k) {ans+=sum[rc];solve(lc,k,bit-1,num);}else {if(lc) insert(nd[lc]|(1<<bit-1));solve(rc,k-(lc?siz[lc]-1:0),bit-1,num|(1<<bit-1));ans-=(1<<bit-1);}return ;
}int main() {read(T);while(T--) {read(n,k),k=n-k,cnt=tr[0][0]=tr[0][1]=0;for(int i=1;i<=n;i++) read(a[i]),insert(a[i]);ans=0,solve(0,k,30,0);write(ans),_E;}return 0;
}
http://www.sczhlp.com/news/4169/

相关文章:

  • 散列表
  • 明锐2012款手动更换曲轴前油封(适用大众EA111发动机)
  • 博客的开始
  • k8s集群部署、以及简易搭建一个wordpress
  • 配置github与windows的ssh连接
  • 2025.8.2总结 - A
  • 2025牛客多校第六场 D.漂亮矩阵 K.最大gcd C.栈 L.最小括号串 个人题解 - CUC
  • Ubuntu20.04 安装 Sophus 库
  • day9
  • yolov3结构详细讲解
  • opencv make 时报错 - 详解
  • 从零开始构建AI Agent评估体系:12种LangSmith评估方法详解
  • Ubuntu20.04 安装 Ceres 库
  • 数据库锁机制有哪些(行锁、表锁、意向锁等)?它们如何影响并发性能?死锁是如何产生的?如何避免或检测死锁?
  • AdminLTE - 完全响应式Bootstrap 5管理仪表盘
  • Ubuntu20.04 安装 Ceres库
  • 第十九篇
  • Odoo18 对接QuickBooks
  • SQL的执行过程,如何优化慢查询
  • Windows 使用本地账号跳过微软账户登录
  • P13546 [OOI 2022] Integral Array 题解
  • How to install pip package
  • 疾跑功能
  • 面向对象三大特性
  • 8.2 - 8.__
  • 题解:CF2129C Interactive RBS
  • 可持久化线段树
  • 在AI技术快速实现创意的时代,挖掘新需求成为关键——某知名Windows依赖分析工具需求探索
  • 某中心将举办机器学习峰会
  • 2.1 变量与常量