因为成为英雄的旅程,往往伴随着|

伊埃斯

园龄:2个月粉丝:5关注:8

2025-02-10日记

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

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

但是现在是日记时间。

Sharing the Sweets

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

对的,他就是不对。洛谷上的题实在是太弱了,所以我们需要一个更好的做法。看到 n,k1500O(nk) 的做法就足够了。

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

dpi,j=k=1jdpij,k

为啥是前几个塔还有为啥是 dpij,k 而不是 dpij,j 呢?

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

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

dpi1,j1=kj1dpij,k

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

dpi,j=dpi1,j1+dpij,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

题意:对于一串合法的括号序列,定义它的深度为括号的最大嵌套层数。比如 ()() 的深度为 1(()()) 的深度为 2((())()) 的深度为 3。有多次询问,询问有 n 对括号,深度为 k 的合法括号序列数量。

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

dpi,j=k=1idpik,j×dpi1,j1

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

注意要用高精度。

#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;
}
posted @   伊埃斯  阅读(5)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起