题目链接: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 ", 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 MBSubmit: 26 Solved: 5
[Submit][Status][Web Board]
Description
Input
Output
Sample Input
4 2
1 1 2 3
Sample Output
11