JZOJ 5414. 【NOIP2017提高A组集训10.22】幸运值

本文介绍了一种解决特定卡牌游戏中幸运值计算的问题。通过分析卡牌数值的异或和,利用组合数学的方法,计算所有可能选择方案的幸运值总和,并给出具体的算法实现。

Description

校庆志愿者小Z在休息时间和同学们玩卡牌游戏。一共有n张卡牌,每张卡牌上有一个数Ai,每次可以从中选出k张卡牌。一种选取方案的幸运值为这k张卡牌上数的异或和。小Z想知道所有选取方案的幸运值之和除以998244353的余数。

Input

输入的第一行有两个整数n和k。
第二行有n个整数,表示序列A。

Output

一个整数表示答案。

Sample Input

输入1:

3 2
1 2 3

输入2:

10 5
123 456 789 987 654 321 101 202 303 404

Sample Output

输出1:

6

输出2:

130776

Data Constraint

对于30%的数据满足,1<=n<=20
对于另30%的数据满足,1<=n<=100,0

Hint

样例1幸运值之和为(1 ⊕ 2) + (1 ⊕ 3) + (2 ⊕ 3) = 6

Solution

  • 我们发现每一位的答案与选了哪些数无关,而与哪一位的0、1数量有关。

  • 于是我们统计出这 N 个数的每一位的0、1数。

  • 对于每一位,我们设有 x1 ,那么则有 nx0

  • 要在其中选 k 个数,又发现其异或和只与 1 的个数的奇偶性有关。

  • 那么这一位的答案即为:

    j=1,j1(mod 2)xCjxCkjnx

  • 即选一些数作为1、另一些数作为0的组合数。

  • 注意特判 Cyx 中当 x<y 时返回 0

  • 时间复杂度为 N log Ai

Code

#include<cstdio>
#include<cmath>
using namespace std;
const int N=1e5+1,mo=998244353;
int mx;
long long ans;
int f[31];
long long g[N],h[N];
inline int read()
{
    int X=0,w=1; char ch=0;
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}
inline long long ksm(long long x,int y)
{
    long long s=1;
    while(y)
    {
        if(y&1) s=s*x%mo;
        x=x*x%mo;
        y>>=1;
    }
    return s;
}
inline long long C(int x,int y)
{
    if(x<y) return 0;
    return g[x]*h[x-y]%mo*h[y]%mo;
}
int main()
{
    int n=read(),k=read();
    for(int i=1;i<=n;i++)
    {
        int x=read(),y=log2(x);
        if(y>mx) mx=y;
        for(int j=0;j<=y;j++) f[j]+=x&1,x>>=1;
    }
    long long p=g[0]=h[0]=1;
    for(int i=1;i<=n;i++) g[i]=g[i-1]*i%mo;
    h[n]=ksm(g[n],mo-2);
    for(int i=n-1;i;i--) h[i]=h[i+1]*(i+1)%mo;
    for(int i=0;i<=mx;i++,p<<=1)
        for(int j=1,q=f[i]<k?f[i]:k;j<=q;j+=2)
            ans=(ans+C(n-f[i],k-j)*C(f[i],j)%mo*p%mo)%mo;
    printf("%lld",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值