关于此题CF2061E_Kevin and And的一些总结

传送门
题目大意:给定n个数a[1...n]m个数b[1...m],并且给定整数k,求让任意i,j使a[i]&b[j]来替代a[i]后这n个数总和最小。

  • 首先我们看到题目给的m范围非常小,最大只有10,然后又问我们k次操作之后总和的最小值,第一反应是不是可以直接先求出每个a[i]分别执行1...k次操作后的最小,然后再用DP来做。然而我们发现,让一个数与上另一个数,得到的结果一定是小于等于当前这个数的(&的运算规则),那也就是说一个数每次和另一个数&操作得到的结果和它本身相比都伴随着减少或者不变,于是乎发现分本用不着DP,只需要直接贪心地每次操作都找减少的最多的那一个就可以了。时间复杂度O(n2m)
  • 这里我们先用状压预处理出b数组中任几个&操作的结果。再往后枚举每个a[i],对于当前a[i],我们都求出其最小的&上1...mb中的数的结果装到一个数组中,最后排序取最大的前k个即可。因为每多一次&操作之后这个数的变小幅度都是在一直减小的,所以在取到对a[i]进行第p次操作的时候,前p1次也一定取到了。
#include<bits/stdc++.h>
    
using namespace std;

long long t;
const long long N = 2e5 + 10;
long long n,m,k,ans;
long long a[N],b[N];
    
void solve() {
    ans = 0;
    cin >> n >> m >> k;
    for(long long i = 1;i <= n;i++) cin >> a[i],ans += a[i];
    for(long long i = 0;i < m;i++) cin >> b[i];
    vector<long long> f(1100);
    f[0] = (1ll << 30) - 1;
    for(long long i = 1;i < (1 << m);i++) {
        long long u = __builtin_ctz(i);
        f[i] = f[i ^ (1 << u)] & b[u];
    }
    vector<long long> h;
    for(long long i = 1;i <= n;i++) {
        vector<long long> g(m + 1);
        for(long long j = 0;j < (1 << m);j++) {
            long long c = __builtin_popcount(j);
            g[c] = max(g[c],a[i] - (a[i] & f[j]));
        }
        for(long long j = 1;j <= m;j++)
            h.emplace_back(g[j] - g[j - 1]);
    }
    
    sort(h.begin(),h.end(),greater<int>());
    for(long long i = 0;i < k;i++) ans -= h[i];
    cout << ans << '\n';
}
    
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin >> t;
    while(t--) solve();
    
    return 0;
}
  • 这里有两个函数__builtin_ctz()__builtin_popcount(),分别求的是最后一个1后有多少个0和整个数有多少个1,类似的还有__builtin_clz()等函数,不仅方便而且性能也很优秀
posted @   孤枕  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
我命令你,喜欢我!