ACM模版

描述

题解

这个题是一个数论题,主要是推公式,但是 dp 同样是可以过的……

先来说 dp(代码 One),设置 dp[i][j][k][r] 表示 %3 后的数 1 出现了 i 次, 2 出现了 j 次,0 出现了 k 次,此时余数为 r 的概率,这样我们就可以轻松转移出来每一个状态了,具体状态转移看代码吧!

说到数论,这个题首先我们可以将所有数 %3 后发现, 0 2 都出现了 n 次, 1 出现了 n+1 次,这里 0 对序列的余数状态无影响,所以我们最后考虑。
此时存在两种情况,以 1 打头、 2 打头,分别考虑下来,
第一种的序列是 11212121212 ,这种情况,很明显,从头到尾都是唯一的,并且是合法的,这里的唯一说的是 %3 后的结果,酱紫,我们知道了每个数所能放的位置,所以只考虑 1 2 的计算结果为 n!(n+1)! ,接着我们可以考虑往其中插入 0 ,由于开头不能插,所以插零的方案数为 (2n+1)(2n+2)3n ,根据乘法原理,得到以 1 打头的情况数为: n!(n+1)!(2n+1)(2n+2)3n
第二种的序列是 22121212111 ,这种情况,我们会发现,当 2 使用完后,会紧接着放 111 ,而这里从第二个开始,就不合法了,所以以 2 打头的情况是无解的。

而所有的排列结果为 (3n+1)! ,所以相除化简后:

n!(n+2)2n(3n+1)

化简形式不唯一,考虑到代码的最简,化成下边的形式最好:
n!(n+1)(n+1)2n(3n+1)

就酱,这个题算是告一段落了~~~

代码

One:

#include <iostream>
#include <cstring>

using namespace std;

const int MAXN = 22;

int n;
double dp[MAXN][MAXN][MAXN][3];

int main()
{
    int T;
    cin >> T;

    while (T--)
    {
        memset(dp, 0, sizeof(dp));

        cin >> n;
        int m = 3 * n + 1;
        for (int i = 0; i <= n + 1; i++)
        {
            for (int j = 0; j <= n; j++)
            {
                for (int k = 0; k <= n; k++)
                {
                    if (i + j + k == 1)
                    {
                        if (i)
                        {
                            dp[i][j][k][1] = (n + 1) * 1.0 / m;
                        }
                        else if (j)
                        {
                            dp[i][j][k][2] = n * 1.0 / m;
                        }
                        continue;
                    }

                    int now = m - i - j - k + 1;
                    dp[i][j][k][1] = (j == 0 ? 0 : dp[i][j - 1][k][2] * (n - j + 1) / now)
                                   + (k == 0 ? 0 : dp[i][j][k - 1][1] * (n - k + 1) / now);
                    dp[i][j][k][2] = (i == 0 ? 0 : dp[i - 1][j][k][1] * (n - i + 2) / now)
                                   + (k == 0 ? 0 : dp[i][j][k - 1][2] * (n - k + 1) / now);
                }
            }
        }

        printf("%.9lf\n", dp[n + 1][n][n][1]);
    }

    return 0;
}

Two:

#include <cstdio>

int n;
double ans;

int main()
{
    int T;
    scanf("%d", &T);

    while (T--)
    {
        scanf("%d", &n);

        ans = (n + 1.0) / (3 * n + 1);
        for (int i = 1; i <= n; i++)
        {
            ans = ans * i / (i + n);
        }

        printf("%.9f\n", ans);
    }

    return 0;
}