Luckyleaves's Blog

stay hungry,stay foolish greedy and lucky

CF425E Sereja and Sets

长久不刷,$dp$有很大退步。

Solution

首先记$f_{i,j}$表示前$f(i)=j$的答案,有以下方程:
$$
f_{i,j}=\sum_{k=1}^{i}f_{k,j-1}\cdot (2^{i-k}-1)\cdot 2^{k\cdot (i-k)}
$$
这个式子是什么意思呢?首先我们考虑划分点$i$和$k$,从$[1,k]$出发延申到$[k+1,i]$中的每条线段都可以要,所以有$2^{k\cdot (i-k)}$条,而由于我们又要新增一条,所以$[k+1,i]\cdots[i,i]$中的所有线段必须至少存在一条,所以有$2^{i-k}-1$种方案。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#pragma GCC optimize("O2,O3,Ofast")
#include<bits/stdc++.h>
#define LL long long
#define PII pair<int, int>
using namespace std;
template <class T>
inline void read(T &res)
{
res = 0; bool flag = 0;
char c = getchar();
while(c < '0' || '9' < c) { if(c == '-') flag = 1; c = getchar(); }
while('0' <= c && c <= '9') res = (res << 3) + (res << 1) + (c ^ 48), c = getchar();
if(flag) res = -res;
}
template <class T, class ...ARC>
inline void read(T &res, ARC &...com) { read(res), read(com...); }
template <class T>
inline void write(T res)
{
if(res < 0) res = -res, putchar('-');
if(res > 9) write(res / 10);
putchar(res % 10 + '0');
}
template <>
inline void write(char s) { putchar(s); }
template <>
inline void write(const char *s) { while(*s) putchar(*s ++); }
template <class T, class ...ARC>
inline void write(T res, ARC ...com) { write(res), write(com...); }
const int N = 505, mod = 1e9 + 7;
int n, m;
int f[N][N], pw[N * N], fac[N];
int main()
{
read(n, m);
pw[0] = 1;
for(int i = 1;i < N * N;i ++) pw[i] = 1ll * 2 * pw[i - 1] % mod;
for(int i = 0;i <= n;i ++) f[i][0] = 1;
for(int k = 1;k <= m;k ++)
{
for(int i = k;i <= n;i ++)
{
for(int j = k - 1;j <= i;j ++)
f[i][k] = (f[i][k] + 1ll * f[j][k - 1] * (pw[i - j] - 1) % mod * pw[j * (i - j)] % mod) % mod;
}
}
write(f[n][m]);
return 0;
}