【数论+组合数学】[省选十连测第十场]基本题

题目描述

【问题描述】
所谓的考试,就一定会有一道基本题给大家送分,而不嵌套题目
的基本题怎么称得上基本呢?
我们可以把基本题具体看成由(),!四种字符组成的一个字符串,
一道基本题是由若干个嵌套题中间加上,分隔组成,而所谓的嵌套题
指 的 是 一 对 括 号 之 间 放 入 若 干 道 由 ! 分 隔 的 基 本 题 。 例 如
(()!(()!())),(()!())就是一道基本题。
当然在出题的时候我们会尽量避免出到重题,嵌套题 A 和 B 相同
指的是 A 中的所有基本题变换顺序之后可以变成 B,例如(()!(()))
与((())!())相同,而基本题 A 和 B 相同指的是将 A 的嵌套题平移后
可 以 变 成 B , 例 如 (),(()),(()!()) 与 (()),(()!()),() 还 有
(()!()),(),(())相同,与(()),(),(()!())不同。
这天小火车找到了 n 个合适的题目来出基本题,他想知道有多少
道恰有 n 对括号的合法且互不相同的基本题。例如当 n=3 时共有 5 个
不同的基本题,分别是“((()))”“(()!())”“((),())”“(()),()”
和“(),(),()”。
【输入格式】
第一行两个整数 t 和 p,其中 t 表示数据组数。保证 p 为质数。
接下来 t 行每行一个整数 n 表示一组询问。
【输出格式】
t 行每行一个整数表示答案,对 p 取模。
【样例输入】
5 2333
1
2
3
4
5
【样例输入】
1
2
5
14
42

分析

qt[i] 表示 i 个括号的嵌套题个数, jb[i] 表示 i 个括号的基本题个数, f[i][j][k] 表示嵌套题内部用i个括号及以下的基本题,k个嵌套题组成的j个括号的基本题(不考虑平移相同)的个数, t[i][j] 表示从i个括号的基本题中选j个出来的方案数(一个元素可以被选多次), t[i][j]=C[jb[i]+j1][j] ,可以用隔板法来验证, g[i][j] 表示内部用i个括号及以下的基本题,j个括号组成的嵌套题个数。

看一下转移

嵌套题的方案和内部基本题的顺序无关

g[i][j]=k=0jig[i1][jki]×t[i][k]qt[i]=g[i1][i1]

基本题的方案和顺序有关,先不考平移的是相同的

f[i][j][k]=l=0min(k,ji)f[i1][jli][kl]×C[k][l]×qt[i]k

如何计算jb[i]呢
然后我们考虑每一种情况在在 f[i][i][j] 中做出的贡献。
相当于一个有j个元素的多重集合的排列,通过循环移位能够变得相同的两个排列视为是相同的,我们要得到每一种不同的排列在 f[i][i][j] 中的贡献。

如果一个排列的循环节的长度是k,那它在 f[i][i][j] 中就会出现k次。
now=f[i][i]
那我们需要怎么计算呢?
我们可以使每一种排列都出现j次。
循环节为k的排列会在 now[kx]x=1,2,3,4 出现,而且贡献都是k次,我们最终只需要使循环节为k的排列的贡献 ×jk 即可。

我们换一种表示,令k为循环节的个数,我们就需要把循环节的个数为k的排列的贡献 ×k

循环节个数为k的排列在 now[d]d|k 也有出现,所以我们要构造一个东西f(i), d|if(d)=d ,狄利克雷卷积为f(i)=i的函数是什么呢?欧拉函数。

所以 jb[i]=ij=1k|gcd(i,j)f[i][i/k][j/k]×ϕ(k)j

然后就能做啦。。。

代码

#include
#include
#include
using namespace std;
#define MAXN 250
int T,n,C[MAXN+10][MAXN+10],phi[MAXN+10],MOD,t[MAXN+10][MAXN+10],qt[MAXN+10],jb[MAXN+10],f[MAXN+10][MAXN+10],inv[MAXN+10];
void prepare(int n){
    int i,j;
    for(i=0;i<=n;i++){
        C[i][0]=1;
        for(j=1;j<=i;j++)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD,phi[i]+=__gcd(i,j)==1;
    }
    inv[0]=inv[1]=1;
    for(i=2;i<=n;i++)
        inv[i]=1ll*(MOD-MOD/i)*inv[MOD%i]%MOD;
}
void calqt(int n){
    static int f[MAXN]={1};
    int j,i,k;
    j=n-1;
    if(j)
        for(i=MAXN-1;i;i--)
            for(k=1;j*k<=i;k++)
                f[i]=(f[i]+1ll*f[i-j*k]*t[j][k])%MOD;
    qt[n]=f[n-1];
}
void caljb(int i,int n){
    int j,k,l,now;
    for(j=n;j;j--)
        for(k=1,now=qt[i];i*k<=j;k++,now=(1ll*now*qt[i])%MOD)
            for(l=k;l<=j;l++)
                f[j][l]=(f[j][l]+1ll*f[j-k*i][l-k]*C[l][k]%MOD*now)%MOD;
    for(j=1;j<=i;j++){
        int s=0;
        for(k=1;k<=j;k++)
            if(i%k==0&&j%k==0)
                s=(s+1ll*f[i/k][j/k]*phi[k])%MOD;
        jb[i]=(jb[i]+1ll*s*inv[j])%MOD;
    }
}
void solve(int n){
    prepare(n);
    int i,j;
    f[0][0]=1;
    for(i=1;i<=n;i++){
        calqt(i),caljb(i,n);
        t[i][0]=1;
        for(j=1;j<=n;j++)
            t[i][j]=1ll*t[i][j-1]*(jb[i]+j-1)%MOD*inv[j]%MOD;
    }
}
int main()
{
    cin>>T>>MOD;
    solve(MAXN);
    while(T--){
        cin>>n;
        cout<
    }
}

你可能感兴趣的:(数论,组合数学)