日记

日记

周一 2月 10 2025
1213 字 · 9 分钟

你说人怎么可以蠢成这个样子

历史总是惊人的相似。我姐在高中的时候看《鬼灭之刃》而我现在也看起来了。
该说不说还挺好看。

但是现在是日记时间。

Sharing the Sweets

题意就是求把 nn 个元素分成 kk 个非空的不论顺序的集合的方案数。这么经典的问题当然在洛谷上也有,但是一看数据范围:这不对吧。

对的,他就是不对。洛谷上的题实在是太弱了,所以我们需要一个更好的做法。看到 n,k1500n,k \le 1500O(nk)\mathcal{O}(nk) 的做法就足够了。

dpi,jdp_{i,j} 表示将共 ii 个元素分成 jj 组的方案数。初始化是 dpi,1=1,i[1,n]dp_{i,1} = 1,i \in [1,n]。答案为 dpn,kdp_{n,k}。考虑如何转移。对于 dpi,jdp_{i,j},我们想象有 jj 座塔,对应 jj 个集合,分配元素就是在第前几个塔上分别垒一个元素。那么有转移:

dpi,j=k=1jdpij,kdp_{i,j} = \sum\limits_{k = 1}^{j}dp_{i - j,k}

为啥是前几个塔还有为啥是 dpij,kdp_{i - j,k} 而不是 dpij,jdp_{i - j,j} 呢?

因为题目里说的是无序,为了保证不算重复,我们就规定前一个塔放的必须比后一个塔多。那么就只能在前几个塔上放。

但是时间复杂度依然有 O(nk2)\mathcal{O}(nk^2),很显然还是不行。我们考虑如何优化。用我们惊人的注意力发现:

dpi1,j1=kj1dpij,kdp_{i - 1,j - 1} = \sum\limits_{k}^{j - 1}dp_{i - j,k}

具体来说就是因为 iji - jiijj 之间的差值,所以他俩同时 1-1 没有影响,再者 kk 是循环项,这就更没有影响了。于是我们的方程可以变成这个样子:

dpi,j=dpi1,j1+dpij,jdp_{i,j} = dp_{i - 1,j - 1} + dp_{i - j,j}

那么这样,这道题就做完了。

可恶的 ICPC 计数 dp 不要模数,害得我还得开 __int128

#include<bits/extc++.h>
#define int __int128
using namespace std;
const int maxn = 1505;
int n,k;
int dp[maxn][maxn];
inline void read(int &x)
{
x = 0;
short f = 1;
char ch = getchar();
while (!isdigit(ch)){f = ch == '-' ? -1 : 1; ch = getchar();}
while (isdigit(ch)){x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
void write(int x)
{
if (x > 10)
write(x / 10);
putchar(x % 10 + '0');
}
signed main()
{
freopen("sweets.in","r",stdin);
freopen("sweets.out","w",stdout);
read(n),read(k);
for (int i = 1; i <= n; i++)
dp[i][0] = dp[i][1] = 1;
for (int i = 2; i <= k; i++)
dp[0][i] = dp[1][i] = 0;
for (int i = 2; i <= n; i++)
for (int j = 2; j <= k; j++)
dp[i][j] = dp[i - 1][j - 1] + (i > j ? dp[i - j][j] : 0);
write(dp[n][k]);
return 0;
}

Little Brackets

题意:对于一串合法的括号序列,定义它的深度为括号的最大嵌套层数。比如 ()() 的深度为 11(()()) 的深度为 22((())()) 的深度为 33。有多次询问,询问有 nn 对括号,深度为 kk 的合法括号序列数量。

dpn,kdp_{n,k} 表示有 nn 对括号,深度 k\le k 的合法括号序列数量。那么每次询问的答案就是 dpn,kdpn,k1dp_{n,k} - dp_{n,k - 1}。初始化 dp0,i=1dp_{0,i} = 1。转移方程如下:

dpi,j=k=1idpik,j×dpi1,j1dp_{i,j} = \sum\limits_{k = 1}^{i}dp_{i - k,j} \times dp_{i - 1,j - 1}

具体来说就是有两种方法以增加括号对数。一种是 ()()()...(),另一种是 (((((...)))))。分别对应两个乘数。然后这道题就做完了。可以预处理所有的 dpdp 值,然后查询就直接算 dpn,kdpn,k1dp_{n,k} - dp_{n,k - 1} 就可以了。

注意要用高精度。

#include<bits/extc++.h>
using namespace std;
struct long_int
{
int len;
short d[105];
long_int(int x = 0)
{
memset(d,0,sizeof d);
len = 0;
while (x)
{
d[++len] = x % 10;
x /= 10;
}
}
void read()
{
char ch = getchar();
while (!isdigit(ch))ch = getchar();
len = 0;
while (isdigit(ch)){d[++len] = ch - '0'; ch = getchar();}
reverse(d + 1,d + len + 1);
}
void write()
{
for (int i = len; i >= 1; i--)
printf("%d",d[i]);
}
friend long_int operator+(const long_int &x,const long_int &y)
{
long_int ret;
ret.len = max(x.len,y.len);
short f = 0;
for (int i = 1; i <= ret.len; i++)
{
ret.d[i] = x.d[i] + y.d[i] + f;
if (ret.d[i] >= 10)
{
f = 1;
ret.d[i] -= 10;
}
else
f = 0;
}
if (f)
ret.d[++ret.len] = f;
return ret;
}
friend long_int operator-(const long_int &x,const long_int &y)
{
long_int ret;
ret.len = max(x.len,y.len);
short f = 0;
for (int i = 1; i <= ret.len; i++)
{
ret.d[i] = x.d[i] - y.d[i] - f;
if (ret.d[i] < 0)
{
f = 1;
ret.d[i] += 10;
}
else
f = 0;
}
while (!ret.d[ret.len])
ret.len--;
return ret;
}
friend long_int operator*(const long_int &x,const long_int &y)
{
long_int ret;
ret.len = x.len + y.len - 1;
for (int i = 1; i <= x.len; i++)
for (int j = 1; j <= y.len; j++)
ret.d[i + j - 1] += x.d[i] * y.d[j];
for (int i = 1; i <= ret.len; i++)
{
ret.d[i + 1] += ret.d[i] / 10;
ret.d[i] %= 10;
}
if (ret.d[ret.len + 1])
ret.len ++;
return ret;
}
}dp[55][55];
void calc()
{
for (int i = 0; i <= 50; i++)
dp[0][i] = 1;
for (int i = 1; i <= 50; i++)
for (int j = 1; j <= 50; j++)
for (int k = 1; k <= i; k++)
dp[i][j] = dp[i][j] + dp[i - k][j] * dp[k - 1][j - 1];
}
signed main()
{
freopen("brackets.in","r",stdin);
freopen("brackets.out","w",stdout);
calc();
int t = 0,n,k;
scanf("%d%d",&n,&k);
while (n || k)
{
if (t)
puts("\n");
printf("Case %d: ",++t);
(dp[n][k] - dp[n][k - 1]).write();
scanf("%d%d",&n,&k);
}
return 0;
}

Thanks for reading!

日记

周一 2月 10 2025
1213 字 · 9 分钟