题解:P8478 「GLR-R3」清明
一个
首先拆贡献:
也就是先给每个点钦定一个父亲然后求乘积,这个值显然只和每个点的儿子个数有关:
令点
由于
那么在
当
考虑从后往前按列进行一个 DP:
第一行即枚举同行的儿子个数
第二行枚举同行的儿子个数
分讨一下
复杂度看着是
#define N 40
int n, m;
int a[N], inv[200];
int val[N][N];
int C(int n, int m)
{
int res = 1;
for(int i = n; i >= n - m + 1; i--) mul_(res, i % mod);
for(int i = 1; i <= m; i++) mul_(res, inv[i]);
return res;
}
namespace Sub1
{
int dp[2][1 << 16], pp[1 << 16], op;
int tmp[N];
void solve()
{
// debug("Sub1\n");
dp[op = 0][0] = 1;
for(int i = 0; i < (1 << m); i++) pp[i] = pp[i >> 1] + (i & 1);
for(int i = n - 1; ~i; i--)
{
op ^= 1;
for(int S = 0; S < (1 << m); S++) if(dp[op ^ 1][S])
{
int v = dp[op ^ 1][S]; dp[op ^ 1][S] = 0;
for(int j = 0; j <= std::min(m + 1, n - i); j++) tmp[j] = 1ll * v * val[i][j] % mod;
int U = S, X = 0;
if(S >> (m - 1) & 1)
U ^= 1 << (m - 1), X ^= 1 << (m - 1);
for(int _T = U, T; ; _T = (_T - 1) & U)
{
T = _T | X;
plus_(dp[op][(S ^ T) << 1 | 1], tmp[pp[T]]);
plus_(dp[op][(S ^ T) << 1], tmp[pp[T] + 1]);
if(!_T) break;
}
}
}
print(dp[op][0], '\n');
}
}
namespace Sub2
{
int dp[2][N][N][N], op;
int C[N][N];
void solve()
{
// debug("Sub2\n");
for(int i = 0; i < N; i++) for(int j = C[i][0] = C[i][i] = 1; j < i; j++)
C[i][j] = sm(C[i - 1][j - 1] + C[i - 1][j]);
dp[op][0][0][0] = 1;
for(int i = m; i >= 0; i--)
{
if(i + m + 1 < n)
{
op ^= 1;
for(int x = 0; x <= m - i; x++) for(int y = 0; y <= m - i; y++) for(int z = 0; z <= i + 1; z++) if(dp[op ^ 1][x][y][z])
{
int v = dp[op ^ 1][x][y][z]; dp[op ^ 1][x][y][z] = 0;
for(int p = 0; p <= y; p++)
{
// 连自环
plus_(dp[op][x][y - p][z], 1ll * v * val[i + m + 1][p + 1] % mod * C[y][p] % mod);
// 向前连
plus_(dp[op][x][y - p + 1][z], 1ll * v * val[i + m + 1][p] % mod * C[y][p] % mod);
// 向上连
if(z) plus_(dp[op][x][y - p][z - 1], 1ll * v * val[i + m + 1][p] % mod * C[y][p] % mod);
}
}
}
if(i < n)
{
op ^= 1;
for(int x = 0; x <= m - i; x++) for(int y = 0; y <= m - i + 1; y++) for(int z = 0; z <= i + 1; z++) if(dp[op ^ 1][x][y][z])
{
int v = dp[op ^ 1][x][y][z]; dp[op ^ 1][x][y][z] = 0;
for(int p = 0; p <= x; p++) for(int q = 0; z + q <= m; q++)
{
// 连自环
plus_(dp[op][x - p][y][z + q], 1ll * v * val[i][p + q + 1] % mod * C[x][p] % mod * C[z + q][q] % mod);
// 向前连
plus_(dp[op][x - p + 1][y][z + q], 1ll * v * val[i][p + q] % mod * C[x][p] % mod * C[z + q][q] % mod);
}
}
}
}
print(dp[op][0][0][0], '\n');
}
} // namespace Sub2
void solve()
{
// memset(h, idx = -1, sizeof(h));
n = read(), m = read();
for(int i = 0; i < n; i++) a[i] = read();
for(int i = 0; i < n; i++)
{
int lim = std::min(m + 1, n - i);
for(int j = 0; j <= lim; j++) val[i][j] = C(a[i] + lim - 1, lim + j - 1);
}
if(m + m + 2 < n) Sub1::solve();
else Sub2::solve();
}