[关闭]
@Falsyta 2015-08-11T01:30:05.000000Z 字数 5384 阅读 1432

mxh带窝打多校之Contest 2

OI mxh带窝飞


约定

题意

1005 Eastest Magical Day Seep Group's Summer

给出无向图G={V,E},求选出一个边的大小为|V|的子集E使得G={V,E}仍然连通。

1008 He is Flying

给出长度为N的非负整数序列A,对于0xNi=1Ai,求

i=1Nj=iN[k=ijAk=x]

1010 JRY is Fighting

有一个初始HP为M的角色在一个N轮的游戏中,在第i轮角色HP会受到攻击减hi(可以为负),在受到攻击后魔瓶中会多k点HP的储备,在第i轮末角色可以决定是否使用魔瓶,使用后会取出魔瓶中的HP储备全部用来恢复角色的HP,角色的HP无上限。令r为使用魔瓶的轮的编号组成的单调递增序列,要求最大化min{riri1|1<i|r|},在此基础上令r字典序最小。

题解

1005 Eastest Magical Day Seep Group's Summer

考虑原问题即为基环树子图的计数。

考虑枚举环上的点的集合S并将其缩成一个点,对于剩下的图做生成树计数,再乘上S连成环的方案数即可。

时间复杂度O(2|V||V|3)

  1. # include <cstdio>
  2. # include <cstring>
  3. # include <iostream>
  4. # include <algorithm>
  5. using namespace std;
  6. # define REP(i, n) for (int i = 1; i <= n; i ++)
  7. # define REP_0(i, n) for (int i = 0; i < n; i ++)
  8. # define FOR(i, a, b) for (int i = a; i <= b; i ++)
  9. # define Mimo(x) if (x >= P) x -= P;
  10. # define RST(a) memset (a, 0, sizeof (a))
  11. const int NR = 18, P = 998244353;
  12. int n, m, deg[NR], inv[40], a[NR][NR], h[NR][1 << 16], ps[1 << 16], bc[1 << 16];
  13. bool g[NR][NR];
  14. inline int Pow (int a, int z) {int s = 1; do {if (z & 1) s = 1ll * s * a % P; a = 1ll * a * a % P;} while (z >>= 1); return s;}
  15. inline int Inv (int x)
  16. {
  17. if (x <= 30) return inv[x];
  18. if (x == P - 1) return inv[0];
  19. return Pow (x, P - 2);
  20. }
  21. int Gauss (int n)
  22. {
  23. int s = 1;
  24. REP (i, n)
  25. {
  26. if (!a[i][i]) FOR (j, i + 1, n) if (a[j][i]) {if (s) s = P - s; REP (k, n) swap (a[i][k], a[j][k]); break;}
  27. if (!a[i][i]) return 0;
  28. FOR (j, i + 1, n)
  29. {
  30. if (!a[j][i]) continue;
  31. int t = 1ll * a[j][i] * Inv (a[i][i]) % P;
  32. REP (k, n) {a[j][k] = a[j][k] - 1ll * t * a[i][k] % P + P; Mimo (a[j][k]);}
  33. }
  34. s = 1ll * s * a[i][i] % P;
  35. }
  36. return s;
  37. }
  38. # define in(a, b) ((b) >> (a) & 1)
  39. int main ()
  40. {
  41. inv[0] = Pow (P - 1, P - 2);
  42. REP (i, 30) inv[i] = Pow (i, P - 2);
  43. while (scanf ("%d%d", &n, &m) != EOF)
  44. {
  45. int t1, t2;
  46. const int M = 1 << n;
  47. REP_0 (i, n) {deg[i] = 0; REP_0 (j, n) g[i][j] = 0; REP_0 (s, M) h[i][s] = 0;}
  48. REP (i, m) scanf ("%d%d", &t1, &t2), t1 --, t2 --, g[t1][t2] = g[t2][t1] = 1, deg[t1] ++, deg[t2] ++;
  49. REP_0 (i, n)
  50. {
  51. ps[1 << i] = i, h[i][1 << i] = 1;
  52. REP (s, M - 1)
  53. {
  54. if ((s == 1 << i) || !in (i, s) || ((s & - s) != (1 << i))) continue;
  55. FOR (j, i + 1, n - 1) if (in (j, s)) {int sa = (s ^ (1 << j)); FOR (k, i, n - 1) if (in (k, sa) && g[k][j]) {h[j][s] += h[k][sa]; Mimo (h[j][s]);}}
  56. }
  57. }
  58. int ans = 0;
  59. REP (s, M - 1)
  60. {
  61. int pt = s & -s;
  62. bc[s] = bc[s ^ pt] + 1;
  63. if (bc[s] < 3) continue;
  64. pt = ps[pt]; int ts = 0;
  65. REP_0 (v, n) if (in (v, s) && g[v][pt]) {ts += h[v][s]; Mimo (ts);}
  66. ts = 1ll * ts * inv[2] % P;
  67. int c0 = 0;
  68. REP_0 (i, n)
  69. {
  70. if (in (i, s)) continue;
  71. c0 ++; int c1 = 0;
  72. REP_0 (j, n) if (!in (j, s)) a[c0][++ c1] = (i == j ? deg[i] : (g[i][j] ? P - 1 : 0));
  73. }
  74. int tmp;
  75. ans = (ans + 1ll * (tmp = Gauss (n - bc[s])) * ts) % P;
  76. }
  77. printf ("%d\n", ans);
  78. }
  79. return 0;
  80. }

1008 He is Flying

考虑用前缀和转成卷积的形式ans[sum[j]sum[i]]=ji即可,对于ji做两次卷积算贡献即可。
可以用long double或CRT。

  1. # include <cstdio>
  2. # include <algorithm>
  3. # include <cmath>
  4. # define REP(i, n) for (int i = 1; i <= n; i ++)
  5. # define REP_0(i, n) for (int i = 0; i < n; i ++)
  6. # define REP_0N(i, n) for (int i = 0; i <= n; i ++)
  7. typedef long long ll;
  8. typedef long double dd;
  9. const dd PI = acosl (-1.0);
  10. const int NR = 101000;
  11. using namespace std;
  12. struct cp
  13. {
  14. dd r, i;
  15. cp () {r = i = 0;}
  16. cp (dd _r, dd _i) {r = _r, i = _i; }
  17. cp operator + (cp b) {return cp (r + b.r, i + b.i); }
  18. cp operator - (cp b) {return cp (r - b.r, i - b.i); }
  19. cp operator * (cp b) {return cp (r * b.r - i * b.i, r * b.i + i * b.r); }
  20. } a[NR << 1], b[NR << 1];
  21. ll ans[NR << 1], su[NR << 1];
  22. int n, s[NR], q0;
  23. void FFT (cp *a, int n, int ty = 1)
  24. {
  25. for (int i = n >> 1, j = 1, k; j < n - 1; j ++)
  26. {
  27. if (i > j) swap (a[i], a[j]);
  28. for (k = n >> 1; k <= i; k >>= 1) i ^= k;
  29. i ^= k;
  30. }
  31. for (int m = 2; m <= n; m <<= 1)
  32. {
  33. int h = m >> 1;
  34. cp wm (cos (PI / h), sin (PI / h) * ty);
  35. for (int i = 0; i < n; i += m)
  36. {
  37. cp w (1, 0);
  38. for (int j = i; j < i + h; j ++)
  39. {
  40. cp u = a[j], v = w * a[j + h];
  41. a[j] = u + v, a[j + h] = u - v, w = w * wm;
  42. }
  43. }
  44. }
  45. if (ty == -1) REP_0 (i, n) a[i].r /= n;
  46. }
  47. int main ()
  48. {
  49. REP (i, 100000) su[i] = su[i - 1] + 1ll * i * (i + 1) / 2;
  50. for (scanf ("%d", &q0); q0; q0 --)
  51. {
  52. scanf ("%d", &n); int N = 1, lz = 0, ta;
  53. ans[0] = 0;
  54. REP (i, n)
  55. {
  56. scanf ("%d", &ta), s[i] = s[i - 1] + ta;
  57. if (!ta) lz ++;
  58. else ans[0] += su[lz], lz = 0;
  59. }
  60. ans[0] += su[lz];
  61. for (; N <= 2 * s[n] + 20; N <<= 1) ;
  62. REP_0 (i, N) a[i] = b[i] = cp (0, 0);
  63. REP (i, n) a[s[i]].r += i;
  64. REP (i, n) b[s[n] - s[i - 1]].r += 1;
  65. FFT (a, N), FFT (b, N); REP_0 (i, N) a[i] = a[i] * b[i]; FFT (a, N, -1);
  66. REP (i, s[n]) ans[i] = (ll) (a[s[n] + i].r + 0.1);
  67. REP_0 (i, N) a[i] = b[i] = cp (0, 0);
  68. REP (i, n) a[s[i]].r += 1;
  69. REP (i, n) b[s[n] - s[i - 1]].r += i - 1;
  70. FFT (a, N), FFT (b, N); REP_0 (i, N) a[i] = a[i] * b[i]; FFT (a, N, -1);
  71. REP (i, s[n]) ans[i] -= (ll) (a[s[n] + i].r + 0.1);
  72. REP_0N (i, s[n]) printf ("%I64d\n", ans[i]);
  73. }
  74. return 0;
  75. }

1010 JRY is Fighting

首先预处理出在第i轮使用魔瓶后最多能撑到轮的编号ri

考虑令

Fi=max{min(Fj,ij)|1j<i,i<rj}

用两个堆分别维护max{Fj}max{ij}即可。
算出Fi后可以知道哪些i可以在最终的ri中,从头扫一遍即可得到最小字典序方案。

  1. # include <cstdio>
  2. # include <cstring>
  3. # include <algorithm>
  4. # include <queue>
  5. using namespace std;
  6. # define REP(i, n) for (int i = 1; i <= n; i ++)
  7. # define REP_D(i, n) for (int i = n; i >= 1; i --)
  8. priority_queue <int> Q0;
  9. priority_queue <pair <int, int> > Q1;
  10. const int NR = 501000, inf = 1 << 30;
  11. int n, m, K, a[NR], s[NR], c[NR], rib[NR], f[NR], li[NR], lim[NR];
  12. bool g[NR];
  13. int main ()
  14. {
  15. while (scanf ("%d%d%d", &n, &m, &K) != EOF)
  16. {
  17. bool surv = 1;
  18. REP (i, n)
  19. {
  20. scanf ("%d", &a[i]), s[i] = s[i - 1] + a[i], c[i] = 0, lim[i] = max (lim[i - 1], s[i]);
  21. if (s[i] >= m + (i - 1) * K) surv = 0;
  22. }
  23. if (!surv) {puts ("Poor Hero!"); continue;}
  24. s[n + 1] = s[n], c[n + 1] = 0;
  25. int ri = 0;
  26. REP (i, n)
  27. {
  28. while (ri < n && m + i * K > s[ri + 1]) ri ++;
  29. rib[i] = ri;
  30. }
  31. int ans = -1;
  32. REP (i, n)
  33. {
  34. f[i] = (m > lim[i] ? inf : 0);
  35. while (!Q0.empty ())
  36. {
  37. int j = -Q0.top ();
  38. if (i > rib[j]) {Q0.pop (); continue;}
  39. if (i - j > f[j]) {Q0.pop (); Q1.push (make_pair (f[j], j)); continue;}
  40. f[i] = max (f[i], i - j); break;
  41. }
  42. while (!Q1.empty ())
  43. {
  44. int j = Q1.top ().second;
  45. if (i > rib[j]) {Q1.pop (); continue;}
  46. f[i] = max (f[i], f[j]); break;
  47. }
  48. if (i * K + m > lim[n]) ans = max (ans, f[i]);
  49. Q0.push (-i);
  50. }
  51. if (ans == inf) {puts ("Poor JRY!"); continue;}
  52. REP_D (i, n)
  53. {
  54. if (i * K + m > lim[n]) {g[i] = 1, c[i] = c[i + 1] + 1; continue;}
  55. g[i] = 0, c[i] = c[i + 1];
  56. int l = i + ans, r = rib[i];
  57. if (l > r || c[r] == c[l + 1]) continue;
  58. g[i] = 1, c[i] ++;
  59. }
  60. int len = 0;
  61. REP (i, n)
  62. if (g[i])
  63. {
  64. li[++ len] = i;
  65. if (i * K + m > lim[n]) break;
  66. i += ans - 1;
  67. }
  68. printf ("%d\n%d\n", ans, len);
  69. REP (i, len) printf ("%d ", li[i]); puts ("");
  70. while (!Q0.empty ()) Q0.pop ();
  71. while (!Q1.empty ()) Q1.pop ();
  72. }
  73. return 0;
  74. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注