洛谷 4424

感觉思路比较神仙。

对于按位与和按位或两种运算,显然每一位是独立的,可以分开考虑。

对于某一位,「与 \(0\)」会将这一位变成 \(0\),「或 \(1\)」会将这一位变成 \(1\) ,「与 \(1\)」和「或 \(0\)」不会改变这一位的值。前两种操作会改变这一位的值,而后两种不会。将前两种称为「关键操作」,那么某一位最终的值取决且仅取决于这一位的最后一次「关键操作」是「与 \(0\)」还是「或 \(1\)」。如果是前者或者不存在关键操作,最终的值就是 \(0\) ,否则是 \(1\)

接下来就比较魔幻了。对于每一位,把每个操作符的右操作数(即题目中给定的 \(a_i\)从右到左 排成一个字符串。假定已经填入了操作符,把这些操作符中与视作 \(1\) ,或视作 \(0\) ,也 从右到左 排成一个字符串。那么,最后一个关键操作就是这两个字符串第一个不相等的地方。换句话说,比较这两个字符串,如果操作符的字符串大(即最后一个关键操作是与 \(0\) ),最终结果就是 \(0\) ,否则是 \(1\)

有了这个奇妙的结论,把每一位的右操作数字符串处理出来,从小到大排序(下文中位的「前」「后」是按照这个顺序排序的)。如果把操作符字符串放在某两位的右操作数字符串之间,那么前面的所有位都是 \(0\) ,后面的所有位都是 \(1\) 。如果 \(r_i\) 中最靠前的 \(1\) 在最靠前的 \(0\) 之前,则无解;否则可以确定合法的操作符字符串在哪两位之间,答案就是字典序在这两位的字符串之间的串的数量。

  1. #include <cstdio>
  2. #include <algorithm>
  3. #include <cstring>
  4. #include <cctype>
  5. #include <string>
  6. using namespace std;
  7. namespace zyt
  8. {
  9. const int N = 1e3 + 10, M = 5e3 + 10, P = 1e9 + 7;
  10. int arr[N][M], val[M], id[M];
  11. basic_string<int> s[M];
  12. bool cmps(const int a, const int b)
  13. {
  14. return s[a] < s[b];
  15. }
  16. int work()
  17. {
  18. int n, m, q;
  19. scanf("%d%d%d", &n, &m, &q);
  20. for (int i = 1; i <= n; i++)
  21. for (int j = 1; j <= m; j++)
  22. scanf("%1d", &arr[i][j]);
  23. for (int i = 1; i <= m; i++)
  24. {
  25. id[i] = i;
  26. for (int j = n, tmp = 0, cnt = 0; j > 0; j--)
  27. {
  28. tmp = tmp * 2 + arr[j][i], ++cnt;
  29. if (j == 1 || cnt == 16)
  30. s[i] += tmp, tmp = cnt = 0;
  31. }
  32. }
  33. sort(id + 1, id + m + 1, cmps);
  34. for (int i = 1; i <= m; i++)
  35. for (int j = n; j > 0; j--)
  36. val[i] = (val[i] * 2LL + arr[j][i]) % P;
  37. val[m + 1] = 1;
  38. for (int i = 1; i <= n; i++)
  39. val[m + 1] = val[m + 1] * 2LL % P;
  40. id[m + 1] = m + 1;
  41. while (q--)
  42. {
  43. bool flag = false, no_ans = false;
  44. static int r[M];
  45. for (int i = 1; i <= m; i++)
  46. scanf("%1d", r + i);
  47. for (int i = 1; i <= m; i++)
  48. if (r[id[i]] == 1)
  49. flag = true;
  50. else
  51. no_ans |= flag;
  52. if (no_ans)
  53. puts("0");
  54. else
  55. {
  56. bool flag = false;
  57. for (int i = 1; i <= m; i++)
  58. if (r[id[i]] == 1)
  59. {
  60. flag = true;
  61. printf("%d\n", (val[id[i]] - val[id[i - 1]] + P) % P);
  62. break;
  63. }
  64. if (!flag)
  65. printf("%d\n", (val[id[m + 1]] - val[id[m]] + P) % P);
  66. }
  67. }
  68. return 0;
  69. }
  70. }
  71. int main()
  72. {
  73. return zyt::work();
  74. }

版权声明:本文为zyt1253679098原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/zyt1253679098/p/12237698.html