ZZUOJ 10509: xor运算统计

题目链接http://acm.zzu.edu.cn:8000/problem.php?id=10509

题目大意:给定n个正整数,a1 a2 ... an,从中选取k个数 , ai1 ai2 ai3 ... Aik,其中(1<=i1<i2<i3<...<ik<=n),u=ai1 ^ai2 ^ai3 ^... ^Aik,将异或和为u 的序列(i1,i2,...,ik)的个数记为f(u),求∑(f(u)*u) (枚举u从1到正无穷) ,由于数值可能非常大,输出对1000000007 取模后的余数(即结果mod 1000000007)。

解题思路:由于ai < 1e6,所以ai的二进制位最多有20位。对每一位来说,假设这一位有xi个1,那么如果要使这一位为1,只需要从xi中选出来奇数个1(假设有k1个),从n-xi中选出来剩余n-k1个零,而这样子的选择方案共有C(xi,1)C(n-xi, k - 1) + C(xi,3)C(n-xi, k - 3) + …… .现在来考虑这样子如何计算结果,假设现在有四个数1 2 3 7, 对应二进制001,010,011,111,从中选k=3个数,那么所有结果总共有

1^2^3 = 0, 1^2^7 = 4, 1^3^7 = 5, 2^3^7 = 6四个。那么显然答案应当是4+5+6。现在将4 5 6这三个数的二进制展开,得到:

4 = 0x1 + 0x2 + 1x4

5 = 1x1 + 0x2 + 1x4

6 = 0x1 + 2x1 + 1x4

到这儿大概已经能够发现,只需要将每一位可选的方案数目乘以其对应的2的幂,最后求和即是要求的答案。

大致过程:扩展gcd求逆元并记录1~1e6的逆元;对于每个xi,使用组合数递推公式C(n,k) = C(n, k - 1) * (n - k + 1) / k 求xi以及n-xi的组合数值,复杂度O(n);最后累加求和即是答案。

代码:

 1 const int maxn = 1e5 + 5;
 2 ll c[maxn], c1[maxn], inv[maxn];
 3 int n, k;
 4 int ti[20], a1[maxn];
 5 
 6 void ext_gcd(ll a, ll b, ll &d, ll &x, ll &y){
 7     if(b == 0){
 8         d = a; x = 1; y = 0;
 9     }
10     else{
11         ext_gcd(b, a % b, d, y, x); 
12         y -= x * (a / b);
13     }
14 }
15 int modinv(ll a){
16     ll x, y, d;
17     ext_gcd(a, -mod, d, x, y);
18     if(d < 0) x = -x;
19     return (x + mod) % mod;
20 }
21 void getinv(){
22     for(int i = 2; i <= 1e5; i++)
23         inv[i] = modinv(i);
24 }
25 void getcom(int x){
26     c[0] = c1[0] = 1; c[1] = x; c1[1] = n - x;
27     for(int i = 2; i <= x; i++) 
28         c[i] = c[i - 1] % mod * (x - i + 1) % mod * inv[i] % mod;
29     for(int i = 2; i <= n - x; i++)
30         c1[i] = c1[i - 1] % mod * (n - x - i + 1) % mod * inv[i] % mod;
31 }
32 void solve(){
33     getinv();
34     memset(ti, 0, sizeof(ti));
35     int mxcnt = 0;
36     for(int i = 1; i <= n; i++){
37         int x = a1[i], cnt = 0;
38         while(x){
39             ti[cnt++] += (x & 1);
40             x >>= 1;
41         }
42         mxcnt = max(mxcnt, cnt);
43     }
44     ll ans = 0;
45     for(int i = 0; i <= mxcnt; i++){
46         int x = ti[i], tm = 1 << i;
47         getcom(x);
48         for(int i = 1; i <= k; i += 2){
49             if(k - i > n - x || i > x) continue;
50             ans = ans + c[i] % mod * c1[k - i] % mod * tm % mod;
51             ans %= mod;
52         }
53     }
54     printf("%lld\n", ans);
55 }
56 int main(){
57     scanf("%d %d", &n, &k);
58     for(int i = 1; i <= n; i++)
59         scanf("%d", &a1[i]);
60     solve();
61 }

题目:

10509: xor运算统计

Time Limit: 5 Sec  Memory Limit: 128 MB
Submit: 26  Solved: 5
[Submit][Status][Web Board]

Description

给定n个正整数,a1 a2 ... an,从中选取k个数 , ai1 ai2 ai3 ... Aik,其中(1<=i1<i2<i3<...<ik<=n),u=ai1 ^ai2 ^ai3 ^... ^Aik,将异或和为u 的序列(i1,i2,...,ik)的个数记为f(u),求∑(f(u)*u) (枚举u从1到正无穷) ,由于数值可能非常大,输出对1000000007 取模后的余数(即结果mod 1000000007)。

 

Input

第一行两个整数n和k (1<=n,k<=10^5)
第二行n个整数,表示a1 a2 ... An. (0<=ai<=10^6)

 

Output

输出一行一个整数,表示最后的答案.

 

Sample Input

4 2
1 1 2 3

Sample Output

11

HINT

 

Source

JXD

posted @ 2017-09-13 11:37  EricJeffrey  阅读(305)  评论(0编辑  收藏  举报