H. Don't Blame Me

原题链接

题解

1.先想想能不能暴力?
发现好像不行,因为不知道哪些元素组合的按位与能恰好有k个1
2.观察数据范围,发现 ai63 也就是说,按位与的结果最大不会大于63 ,即 6 位 1 ,这暗示着我们可能可以从这里入手,即遍历所有按位与的情况,然后判断每种有k个1的按位与,有几个子序列能达到
3.对于每种有k个1的按位与,如何得出有几个子序列能达到它呢?
这里就是状态的巧妙之处了
所有子序列 = i=1nSi 其中 Si 是以 i 为结尾的子序列
dp[i][j] 有多少个结尾不大于 i 的子序列(前缀和的思想好像在dp里经常出现), 且其按位与能达到 j
dp[i][j & ai]=dp[i1][j & ai]+dp[i1][j]
即若已知前 i 个数,按位与能达到 jai & j 的子序列个数,那么前 i+1 个数,按位与能达到 ai & j 的子序列个数也已知

code

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
int a[200005]={0};
int dp[200005][70]={0};
int main()
{
    ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t;
    cin>>t;
    while(t--)
    {
        int n,k;
        cin>>n>>k;

        for(int i=1;i<=n;i++) cin>>a[i];

        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=63;j++) dp[i][j] = dp[i-1][j];
            for(int j=63;j>=0;j--)
            {
                dp[i][j & a[i]] = (dp[i][j & a[i]] + dp[i-1][j]) % mod;
            }
            dp[i][a[i]] = (dp[i][a[i]] + 1LL) % mod;
        }

        int ans=0;
        for(int i=63;i>=0;i--)
        {
            int cnt=0;
            int tem=i;
            while(tem)
            {
                cnt += (tem % 2LL);
                tem >>= 1LL;
            }
            if(cnt == k) ans = (ans + dp[n][i]) % mod;
        }
        cout << ans << endl;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=63;j++) dp[i][j]=0;
        }
    }
    return 0;
}

posted @   纯粹的  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示