abcdeffa's Blog

当局者迷,旁观者清。

0%

GMOJ S5414 【幸运值】

Description

有 $n$ 张卡牌,第 $i$ 张卡牌上有一个数 $a_i$,每次可以从中选出 $k$ 张卡牌。一种选取方案的幸运值为这 $k$ 张卡牌上数的异或和。试求出所有选取方案的幸运值之和除以 $998244353$ 的余数。

Solution

看到异或,想到每一个二进制位的贡献是独立的,于是可以想到按位讨论。

再看一下 $a_i$ 的数据范围中那明显的提示,嗯是了。

于是考虑枚举这一个二进制位上选的 1 的个数,再用组合数随便计算一下即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <cstdio>
#define mod 998244353
#define maxN 100010
long long cnt[50];
long long inv_fac[maxN], fac[maxN], a[maxN];
long long C (long long n, long long m)
{
return fac[n] * inv_fac[m] % mod * inv_fac[n - m] % mod;
}
long long pow (long long x, long long y)
{
if(!y)
{
return 1;
}
else
{
long long dq = pow(x, y >> 1);
if(!(y % 2))
{
return dq * dq % mod;
}
else
{
return dq * dq % mod * x % mod;
}
}
}
int main ()
{
long long n = 0, k = 0;
scanf("%lld %lld", &n, &k);
for(long long i = 1;i <= n; i++)
{
scanf("%lld", &a[i]);
}
fac[0] = 1, inv_fac[0] = 1;
for(long long i = 1;i <= n; i++)
{
fac[i] = fac[i - 1] * i % mod;
inv_fac[i] = pow(fac[i], mod - 2);
}
for(long long T = 0;T <= 32; T++)
{
for(long long i = 1;i <= n; i++)
{
cnt[T] += ((a[i] & (1LL << T)) > 0);
}
}
long long Ans = 0;
for(long long T = 0;T <= 32; T++)
{
for(long long i = 1;i <= cnt[T]; i += 2)
{
long long x = i, y = k - i;
if(i > k)
{
break;
}
if(cnt[T] + y > n)
{
continue;
}
Ans += C(cnt[T], x) * C(n - cnt[T], y) % mod * (1LL << T) % mod;
Ans %= mod;
}
}
printf("%lld", Ans);
return 0;
}