题解:P8984 [北大集训 2021] 末日魔法少女计划
太牛了!
首先这个考虑这些矩阵相关的条件是什么意思。
考察矩阵乘法的定义,令
再基于
- 给定
n - 1 个半群信息; - 还允许预处理,对已有半群信息进行至多
m 次合并; - 要求:对于所有区间
[l, r] ,能够在已知的半群信息中选出至多k 个进行合并,来得到这个区间的半群信息。
那么
对于
- 所有子结点
[l, r] 需要满足:能够在k 次合并以内表示出[l, r] 内的任意区间半群信息; - 有
B - 1 个结点[l, r] 需要满足,能直接给出前缀[l, p] 的半群信息; - 有
B - 1 个结点[l, r] 需要满足,能直接给出后缀[p, r] 的半群信息。
设计 dp,跑出最优状态就可以了。具体的,设
// Think TWICE, code ONCE
#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline ll Read() {
int sig = 1; ll num = 0; char c = getchar();
while(!isdigit(c)) { if(c == '-') sig = -1; c = getchar(); }
while(isdigit(c)) num = (num << 3) + (num << 1) + (c ^ 48), c = getchar();
return num * sig;
}
void Write(ll x) {
if(x < 0) putchar('-'), x = -x;
if(x > 9) Write(x / 10);
putchar((x % 10) ^ 48);
}
const int N = 2005, K = 16, inf = 1e9;
int f[K][N], g[K][N], pl[K][N], pr[K][N], q[K][N], a[N][N];
void Construct(int k, int n, vector<int> vec) {
if(n <= 1) return ;
if(k == 1) {
for(auto v : vec) for(auto w : vec) if(w > v) a[v][w] = 1;
return ;
}
int i, j, x = n - pl[k][n] - pr[k][n], y = q[k][x], l, r;
vector<int> bsiz(1, pl[k][n]);
for(i = 1; i <= y; i++) bsiz.emplace_back(x / y + (i <= x % y));
bsiz.emplace_back(pr[k][n]);
// cerr << "Construct " << k << ' ' << n << " | ";
// for(auto v : bsiz) cerr << v << ' ';
// cerr << "| ";
// for(auto v : vec) cerr << v << ' ';
// cerr << endl;
for(i = 0, l = 0; i <= y; l += bsiz[i], i++) {
r = l + bsiz[i];
for(j = l; j <= r; j++) a[vec[j]][vec[r]] = 1;
}
for(i = 1, l = bsiz[0]; i <= y + 1; l += bsiz[i], i++) {
r = l + bsiz[i];
for(j = l; j <= r; j++) a[vec[l]][vec[j]] = 1;
}
for(i = 0, l = 0; i <= y + 1; l += bsiz[i], i++) {
int siz = bsiz[i]; vector<int> nvec; r = l + bsiz[i];
for(j = l; j <= r; j++) nvec.emplace_back(vec[j]);
if(i <= y && siz) siz--, nvec.pop_back();
if(i && siz) siz--, nvec.erase(nvec.begin());
Construct(k, siz, nvec);
}
vector<int> nvec;
for(i = 1, j = bsiz[0]; i <= y + 1; j += bsiz[i], i++) nvec.emplace_back(vec[j]);
Construct(k - 2, y, nvec);
}
namespace checker {
int b[N][N], c[N][N];
void Check(int n, int k) {
int i, p, q, r;
for(p = 0; p <= n; p++) b[p][p] = 1;
for(i = 1; i <= k; i++) {
for(p = 0; p <= n; p++) for(q = 0; q <= n; q++) for(r = 0; r <= n; r++) {
c[p][r] |= b[p][q] && a[q][r];
}
memcpy(b, c, sizeof(b)), memset(c, 0, sizeof(c));
}
for(p = 0; p <= n; p++) for(q = 0; q <= n; q++) cerr << b[p][q] << (q == n ? '\n' : ' ');
for(p = 0; p <= n; p++) for(q = p; q <= n; q++) assert(b[p][q]);
}
}
int main() {
int i, j, t, n, k; vector<int> vec;
n = Read(), k = Read(), g[1][1] = g[2][1] = inf;
for(i = 3; i <= k; i++) q[i][1] = 1;
for(i = 2; i <= n; i++) {
f[1][i] = g[1][i] = (i - 1) * i / 2;
for(j = 2; j <= k; j++) {
f[j][i] = g[j][i] = inf;
for(t = 1; t <= i; t++) {
if(t * 2 <= i) {
int val = (f[j][t - 1] + t - 1) * 2 + g[j][i - t * 2];
if(f[j][i] > val) f[j][i] = val, pl[j][i] = pr[j][i] = t;
}
if(t * 2 + 1 <= i) {
int val = (f[j][t - 1] + f[j][t] + t * 2 - 1) + g[j][i - t * 2 - 1];
if(f[j][i] > val) f[j][i] = val, pl[j][i] = pr[j][i] = t, pr[j][i]++;
}
if(j > 2) {
int val = f[j - 2][t];
val += (f[j][max(i / t - 2, 0)] + max(0, i / t * 2 - 3)) * (t - i % t);
val += (f[j][max(i / t - 1, 0)] + max(0, i / t * 2 - 1)) * (i % t);
if(g[j][i] > val) g[j][i] = val, q[j][i] = t;
}
}
}
}
for(i = 0; i <= n; i++) a[i][i] = a[i][i + 1] = 1, vec.emplace_back(i);
int m = 0; vector<pair<int, int> > res; Construct(k, n, vec);
for(i = 0; i <= n; i++) for(j = i + 2; j <= n; j++) if(a[i][j]) m++, res.emplace_back(i, j);
Write(m), putchar('\n');
for(auto v : res) Write(v.first), putchar(' '), Write(v.second), putchar('\n');
// checker::Check(n, k);
}