1028D basket
提供一个 \(\mathcal O(NK)\) 做法。
考虑同其他题解,建图
\[x\to x-2,\qquad x\to x+K,\qquad x\in[2,n]
\]
则被删去的点集合法,等价于导出子图无环。
对偶数 \(K\),奇偶独立,相当于计数不包含连续 \(\frac K2+1\) 个 \(1\) 的二进制串,分开做完乘起来即可。
对奇数 \(K\),令 \(h=\frac {K+1}{2}\),把同奇偶、间距为 \(2\) 的连续选中点称为一段。
两段异奇偶的区间 $ [a,b],[c,d] $ 能够形成环,当且仅当 $ a+K\le d$ 且 \(c+K\le b\)。而且可以证明只要存在任意环,就一定存在这样的两段。
从小到大扫描顶点,维护两条奇偶序列当前极长连续 \(1\) 段:
长度至少 \(h+1\) 的段称为长段。任何时刻不可能同时存在两条长段,否则成环。
在此基础上,设 DP 状态:
- \(f(u,v)\):表示两段均为短段,满足 \(0\le u,v\le h\)。
- \(g(j,d)\):表示奇数段长 \(j\),偶数段长度 \(\ge h+1\),最多还能再延长 \(d\) 次(绿色段)。这里的 \(d\) 在偶数段超过 \(h\) 的那一刻(红色)计算。此后的 \(j\) 变化不影响 \(d\),也不用保存偶数段实际长度。
若不存在这样的奇数段,置 \(d\) 为 \(\infty\) 即可。

- \(h(d,j)\):与 \(g\) 对称,表示奇数是长段。
总状态数为 \(O(K^2)\),每个状态只有填 \(0/1\) 两种转移。
至此得到一个 \(O(NK^2)\) 时间、\(O(K^2)\) 空间 的做法。
进一步观察到,所有转移只有以下几种形式:
- 在短段、长段之间转移 \(O(K)\) 个边界状态
- 将某一维的所有状态求和,放到坐标 \(0\)
- 取出或清除边界行、边界列
- 某一维整体加一或减一
所以维护 二维表的循环行、列下标 以及 每行、每列的模 \(M\) 总和。整体平移只修改下标,清除、替换一行或一列是 \(O(K)\),边界转移也是 \(O(K)\)。
即可做到时间 \(\mathcal O(NK)\),空间 \(\mathcal O(K^2)\)。
欢迎品鉴代码(
#include <bits/stdc++.h>
typedef long long ll;
int n, k, h, A, su, sv, lu, ld, ou, od; ll M;
ll S[505][505], L[505][505], O[505][505];
ll RS[505], CS[505], RL[505], CL[505], RO[505], CO[505], IL[505], IO[505],
NS[505], MS[505], NL[505], ML[505], NO[505], MO[505], iL[505], iO[505],
rH[505], cH[505], fL[505], fO[505], z0[505], z1[505];
ll md(ll x) { x %= M; return x < 0 ? x + M : x; }
ll &sx(int u, int v) { return S[(u + su) % A][(v + sv) % A]; }
ll &lx(int j, int d) { return L[(j + lu) % A][(d + ld) % h]; }
ll &ox(int i, int d) { return O[(i + ou) % h][(d + od) % h]; }
ll pw(ll b, ll e) { ll r = 1 % M; for (b %= M; e; e >>= 1, b = b * b % M) if (e & 1) r = r * b % M; return r; }
ll nr(int q) {
static ll f[1005]; f[0] = 1 % M; ll w = 0;
for (int i = 1; i <= q; i++) { w = md(w + f[i - 1] - (i > h + 1 ? f[i - h - 2] : 0)); f[i] = md(w + (i <= h)); }
return f[q];
}
ll odd() {
h = (k + 1) / 2; A = h + 1; su = sv = lu = ld = ou = od = 0;
for (int i = 0; i < A; i++) { memset(S[i], 0, sizeof(ll) * A); memset(L[i], 0, sizeof(ll) * h); }
for (int i = 0; i < h; i++) memset(O[i], 0, sizeof(ll) * h);
for (ll *p : {RS, CS, RL, CL, RO, CO, IL, IO}) memset(p, 0, sizeof(ll) * (h + 1));
ll TS = 1 % M, TL = 0, TO = 0; sx(0, 0) = RS[0] = CS[0] = TS;
for (int t = 1; t <= (n - 1) / 2; t++) {
for (int v = 0; v <= h; v++) rH[v] = sx(h, v);
for (int u = 0; u <= h; u++) cH[u] = sx(u, h);
for (int j = 0; j <= h; j++) { z0[j] = lx(j, 0); lx(j, 0) = 0; }
for (int d = 1; d < h; d++) { CL[d] = md(CL[d] - lx(h, d)); lx(h, d) = 0; }
CL[0] = 0;
for (int j = 0; j <= h; j++) { iL[j] = IL[j]; IL[j] = 0; }
for (int i = 0; i < h; i++) { z1[i] = ox(i, 0); ox(i, 0) = 0; }
for (int d = 1; d < h; d++) { CO[d] = md(CO[d] - ox(h - 1, d)); ox(h - 1, d) = 0; }
CO[0] = 0;
for (int i = 0; i < h; i++) { iO[i] = IO[i]; IO[i] = 0; }
for (int j = 0; j <= h; j++) IL[j] = md(iL[j ? j - 1 : 0] + rH[j ? j - 1 : 0]);
IO[0] = md(iO[0] + RL[h] + CS[h]);
for (int i = 1; i < h; i++) IO[i] = md(iO[i - 1] + cH[i - 1]);
for (int w = 0; w < h; w++) fL[w] = fO[w] = 0;
for (int w = 0; w < h - 1; w++) { fL[w] = md(CL[w + 1] + iL[h - w - 1] + rH[h - w - 1]); fO[w] = md(CO[w + 1] + (w < h - 2 ? iO[h - w - 2] : 0)); }
su = (su + A - 1) % A; sv = (sv + A - 1) % A;
if (h > 1) { lu = (lu + A - 1) % A; ld = (ld + 1) % h; ou = (ou + h - 1) % h; od = (od + 1) % h; }
ll T = md(TS + TL + TO), a = T, b = T;
sx(0, 0) = T;
for (int w = 1; w <= h; w++) { sx(0, w) = md(CS[w - 1] + RL[w - 1]); sx(w, 0) = md(RS[w - 1] + RO[w - 1]); a = md(a + CS[w - 1] + RL[w - 1]); b = md(b + RS[w - 1] + RO[w - 1]); }
for (int w = 0; w < h; w++) { lx(0, w) = fL[w]; ox(0, w) = fO[w]; }
NS[0] = a; MS[0] = b;
for (int u = 0; u < h; u++) { NS[u + 1] = md(2 * RS[u] + RO[u] - cH[u]); MS[u + 1] = md(2 * CS[u] + RL[u] - rH[u]); }
TS = 0; for (int u = 0; u <= h; u++) TS = md(TS + NS[u]);
a = IL[0]; b = IO[0];
for (int w = 0; w < h; w++) { a = md(a + fL[w]); b = md(b + fO[w]); }
NL[0] = a; NO[0] = b;
for (int j = 1; j <= h; j++) NL[j] = md(RL[j - 1] - iL[j - 1] - z0[j - 1] + IL[j]);
for (int i = 1; i < h; i++) NO[i] = md(RO[i - 1] - iO[i - 1] - z1[i - 1] + IO[i]);
for (int w = 0; w < h; w++) { ML[w] = md((w < h - 1 ? CL[w + 1] : 0) + fL[w]); MO[w] = md((w < h - 1 ? CO[w + 1] : 0) + fO[w]); }
TL = 0; for (int j = 0; j <= h; j++) TL = md(TL + NL[j]);
TO = 0; for (int i = 0; i < h; i++) TO = md(TO + NO[i]);
memcpy(RS, NS, sizeof(ll) * (h + 1)); memcpy(CS, MS, sizeof(ll) * (h + 1)); memcpy(RL, NL, sizeof(ll) * (h + 1));
memcpy(RO, NO, sizeof(ll) * h); memcpy(CL, ML, sizeof(ll) * h); memcpy(CO, MO, sizeof(ll) * h);
}
if (n & 1) return md(TS + TL + TO);
ll ans = 0;
for (int u = 0; u <= h; u++) for (int v = 0; v <= h; v++) { ll x = sx(u, v); if (x) ans = (ans + x * ((u == h && v == h) ? 1 : 2)) % M; }
for (int j = 0; j <= h; j++) {
for (int d = 0; d < h; d++) { ll x = lx(j, d); if (x) ans = (ans + x * ((d && j < h) ? 2 : 1)) % M; }
ans = (ans + IL[j] * (j < h ? 2 : 1)) % M;
}
for (int i = 0; i < h; i++) {
for (int d = 0; d < h; d++) ans = (ans + ox(i, d) * 2) % M;
ans = (ans + IO[i] * 2) % M;
}
return md(ans);
}
ll sol(int N, int K) {
n = N; k = K;
if (!(K & 1)) { h = K / 2; ll a = nr(n / 2), b = nr((n - 1) / 2); return a * b % M; }
if (K >= n - 2) return pw(2, n - 1);
return odd();
}
int main() {
freopen("basket.in", "r", stdin); freopen("basket.out", "w", stdout);
int T; if (scanf("%d", &T) != 1) return 0;
while (T--) { int N, K; scanf("%d%d%lld", &N, &K, &M); printf("%lld\n", sol(N, K)); }
return 0;
}
本文来自博客园,作者:CuteNess,转载请注明原文链接:https://www.cnblogs.com/CuteNess/p/23469260

浙公网安备 33010602011771号