[关闭]
@Chilling 2017-07-09T11:39:57.000000Z 字数 2687 阅读 948

HDU-5119: Happy Matt Friends

DP 高斯消元


Problem Description

Matt has N friends. They are playing a game together.

Each of Matt’s friends has a magic number. In the game, Matt selects some (could be zero) of his friends. If the xor (exclusive-or) sum of the selected friends’magic numbers is no less than M , Matt wins.

Matt wants to know the number of ways to win.

Input

The first line contains only one integer T , which indicates the number of test cases.

For each test case, the first line contains two integers .

In the second line, there are N integers , indicating the i-th friend’s magic number.

Output

For each test case, output a single line “Case #x: y”, where x is the case number (starting from 1) and y indicates the number of ways where Matt can win.

Sample Input

2
3 2
1 2 3
3 3
1 2 3

Sample Output

Case #1: 4
Case #2: 2

Hint

In the first sample, Matt can win by selecting:
friend with number 1 and friend with number 2. The xor sum is 3.
friend with number 1 and friend with number 3. The xor sum is 2.
friend with number 2. The xor sum is 2.
friend with number 3. The xor sum is 3. Hence, the answer is 4.

题意: t组数据,每组输入一个n和m,然后输入n个数,要从这n个数中随便选一些出来,让他们的异或值大于等于m,求方案数量。

分析:由于时间和内存都开得比较大,可以考虑直接暴力求解。也可以用高斯消元。


DP : dp[i][j]表示前i个数异或起来的值为j的方案数是多少,那么就可以得到dp[i][j]=dp[i-1][j]+dp[i-1][j^a[i]],前i-1个数异或等于j的个数加上前i-1个数异或起来为j^a[i]的个数。因为j^a[i]^a[i]=j
可以用滚动数组优化,由于dp只与其前一个状态有关系,因此得到dp[i][j]=dp[i^1][j]+dp[i^1][j^a[i]]

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long LL;
  4. const int maxn = 1 << 20;
  5. int a[50];
  6. LL dp[2][maxn];
  7. //int dp[50][maxn];
  8. int main()
  9. {
  10. int t, o = 0;
  11. scanf("%d", &t);
  12. while(t--)
  13. {
  14. int n, m;
  15. scanf("%d%d", &n, &m);
  16. for(int i = 1; i <= n; i++)
  17. scanf("%d", &a[i]);
  18. memset(dp, 0, sizeof(dp));
  19. dp[0][0] = 1;
  20. int cur = 1;
  21. for(int i = 1; i <= n; i++)
  22. {
  23. for(int j = 0; j < maxn; j++)
  24. {
  25. //dp[i][j]=dp[i-1][j]+dp[i-1][j^a[i]];
  26. dp[cur][j] = dp[cur ^ 1][j] + dp[cur ^ 1][j ^ a[i]];
  27. }
  28. cur ^= 1;
  29. }
  30. LL ans = 0;
  31. printf("Case #%d: ", ++o);
  32. for(int i = m; i < maxn; i++)
  33. ans += dp[cur ^ 1][i];
  34. printf("%lld\n", ans);
  35. }
  36. return 0;
  37. }

高斯消元: 求出小于m的个数,用总数减去它即可。具体做法:首先把每个数拆成二进制存在二维数组里,每个数存一列,从上到下为高位到低位,m放在最后一列。一行一行处理,如果某行m的二进制为1,那么将它变为0,处理这一行及其以上的矩阵,,ans为总方案数。
每次都要重新构造矩阵。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long LL;
  4. int mp[55][55];
  5. int tp[55][55];
  6. int gauss(int A[][55], int n, int m)
  7. {
  8. int r = 0;
  9. for(int i = 0; i < m && r < min(n, m); i++)
  10. {
  11. for(int j = r; j < n; j++)
  12. if(A[j][i])
  13. {
  14. for(int k = i; k < m; k++)
  15. swap(A[j][k], A[r][k]);
  16. break;
  17. }
  18. if(A[r][i] == 0) continue;
  19. for(int j = 0; j < n; j++)
  20. {
  21. if(j != r && A[j][i])
  22. for(int k = i; k < m; k++)
  23. A[j][k] ^= A[r][k];
  24. }
  25. r++;
  26. }
  27. if(r == 0) return 0;
  28. for(int i = 0; i < m - 1; i++)
  29. if(A[r - 1][i] != 0)
  30. return r;
  31. return -1;
  32. }
  33. int main()
  34. {
  35. int t,o=0;
  36. scanf("%d", &t);
  37. while(t--)
  38. {
  39. int n, m, x;
  40. memset(tp, 0, sizeof(tp));
  41. scanf("%d%d", &n, &m);
  42. for(int i = 0; i < m; i++)
  43. {
  44. int k;
  45. scanf("%d",&k);
  46. while(k--)
  47. {
  48. scanf("%d", &x);
  49. tp[x - 1][i] = 1;
  50. }
  51. }
  52. int q;
  53. scanf("%d", &q);
  54. printf("Case %d:\n",++o);
  55. while(q--)
  56. {
  57. memcpy(mp, tp, sizeof(tp));
  58. for(int i = 0; i < n; i++)
  59. {
  60. scanf("%d", &x);
  61. mp[i][m] = x;
  62. }
  63. int cnt=gauss(mp,n,m+1);
  64. LL ans=1LL<<(m-cnt);
  65. if(cnt==-1)
  66. ans=0;
  67. printf("%lld\n",ans);
  68. }
  69. }
  70. return 0;
  71. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注