2025-02-10日记
你说人怎么可以蠢成这个样子
历史总是惊人的相似。我姐在高中的时候看《鬼灭之刃》而我现在也看起来了。
该说不说还挺好看。
但是现在是日记时间。
Sharing the Sweets
题意就是求把 个元素分成 个非空的不论顺序的集合的方案数。这么经典的问题当然在洛谷上也有,但是一看数据范围:这不对吧。
对的,他就是不对。洛谷上的题实在是太弱了,所以我们需要一个更好的做法。看到 , 的做法就足够了。
设 表示将共 个元素分成 组的方案数。初始化是 。答案为 。考虑如何转移。对于 ,我们想象有 座塔,对应 个集合,分配元素就是在第前几个塔上分别垒一个元素。那么有转移:
为啥是前几个塔还有为啥是 而不是 呢?
因为题目里说的是无序,为了保证不算重复,我们就规定前一个塔放的必须比后一个塔多。那么就只能在前几个塔上放。
但是时间复杂度依然有 ,很显然还是不行。我们考虑如何优化。用我们惊人的注意力发现:
具体来说就是因为 是 和 之间的差值,所以他俩同时 没有影响,再者 是循环项,这就更没有影响了。于是我们的方程可以变成这个样子:
那么这样,这道题就做完了。
可恶的 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
题意:对于一串合法的括号序列,定义它的深度为括号的最大嵌套层数。比如 ()()
的深度为 ,(()())
的深度为 ,((())())
的深度为 。有多次询问,询问有 对括号,深度为 的合法括号序列数量。
设 表示有 对括号,深度 的合法括号序列数量。那么每次询问的答案就是 。初始化 。转移方程如下:
具体来说就是有两种方法以增加括号对数。一种是 ()()()...()
,另一种是 (((((...)))))
。分别对应两个乘数。然后这道题就做完了。可以预处理所有的 值,然后查询就直接算 就可以了。
注意要用高精度。
#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;
}
本文来自博客园,使用 CC BY-NC-SA 4.0 协议。
作者:伊埃斯,转载请注明原文链接:https://www.cnblogs.com/Eous/p/18708733
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步