你说人怎么可以蠢成这个样子
历史总是惊人的相似。我姐在高中的时候看《鬼灭之刃》而我现在也看起来了。
该说不说还挺好看。
但是现在是日记时间。
Sharing the Sweets↗
题意就是求把 个元素分成 个非空的不论顺序的集合的方案数。这么经典的问题当然在洛谷↗上也有,但是一看数据范围:这不对吧。
对的,他就是不对。洛谷上的题实在是太弱了,所以我们需要一个更好的做法。看到 , 的做法就足够了。
设 表示将共 个元素分成 组的方案数。初始化是 。答案为 。考虑如何转移。对于 ,我们想象有 座塔,对应 个集合,分配元素就是在第前几个塔上分别垒一个元素。那么有转移:
为啥是前几个塔还有为啥是 而不是 呢?
因为题目里说的是无序,为了保证不算重复,我们就规定前一个塔放的必须比后一个塔多。那么就只能在前几个塔上放。
但是时间复杂度依然有 ,很显然还是不行。我们考虑如何优化。用我们惊人的注意力发现:
具体来说就是因为 是 和 之间的差值,所以他俩同时 没有影响,再者 是循环项,这就更没有影响了。于是我们的方程可以变成这个样子:
那么这样,这道题就做完了。
可恶的 ICPC 计数 dp 不要模数,害得我还得开 __int128。
#include<bits/extc++.h>#define int __int128using 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;}