君と彼女の恋

来源:互联网 发布:上海爱回收java 编辑:程序博客网 时间:2024/05/01 14:02

题解前的BB
题目居然用漫作为题目背景,题目中那神说的话不符合语法,我也是醉了。

题目大意
给出n,m(0n1018,1m100),有序列a1,a2,a3...ak1,ak满足这些数的和是n,且每个数模m后的结果互不相同,求这样的序列的个数,结果模905229641。

我们先来学习一些必备的东西。

放小球问题
现在我们来考虑一类特殊的问题:现在有a个球,要将其分成b组,每组至少有一个球,求方案数。
我们把a个球摊开后,题目就变成,要在a个格子之间放b-1个隔板(即在a1个空隙中放入b1隔板),使其分成b组,那么这样的组合数就为Cb1a1

这个问题再升级一下就变成:现在有a个球,要将其分成b组,每组可以没有球,求方案数。
这样的解法其实是类似的。
考虑一个合法的方案,在每一组里都加入一个球,则每一组都有至少一个球,就得球的个数就变成了a+b个,题目就变成了:有a+b个球,要将其分成b组,每组至少有一个球,求方案数。则由上面一个问题的结论得出,这个问题的答案即为Cb1a+b1

逆元
大家都知道倒数吧,1xx的倒数,那么在模意义下的倒数即为逆元,即如果满足x×x1(modp),那么k就是x的逆元,
由费马小定理:当xp互质时,xp11(modp)
x×xp21(modp)
又因为x×x1(modp)
那么xxp2(modp)
x的逆元为xp2

思路
一个很直观的想法是设fx,y表示这个序列的数的和是x,有y个模m后结果不同的数的方案数,显然转移伪代码为

t=0 -> m-1    k=0 -> n    if k%t==0        x=n -> 0            y=1 -> m            f[x][y]=f[x][y]+f[x-k][y-1];

算出f数组后直接统计答案就好了。
然而,这样的时间复杂度是O(n2m2),显然很暴力,空间也很大。
这个方程是可以优化的!!!,设fx,y表示这个序列的数模m后的和为x,有y个模m后结果不同的数的方案数,这样的话,转移就变成了

t=0 -> m-1    x=m*(m-1)/2 -> 0        y=1 -> m        f[x][y]=f[x][y]+f[x-t][y-1];

这样的时间复杂度就变成了O(m4),时限一秒,可以过。但是,还没有完,我们还要求答案。

回归题目
前面我们已经预处理出了f数组,然后在于处理出jsi表示i的阶乘。设有序列b1,b2...bk满足b序列的数互不相同且都小于m,求答案的具体思路就是枚举b序列的和t,显然我们可以得出还需给任意一些b序列里的数加上共((nt)÷m)个m,就能使b序列成为一个合法的a序列,设x=(nt)÷m,再枚举b序列的长度len,那么再由前面讨论出的特殊问题的结论(看到这里读者也许忘了,就是将a个球分成b组,每组可以没有球的方案数为Cb1a+b1),答案就为ft,lenjslenClen1x+len1的和
看到这儿,是不是觉得这就是正解,小心脏就兴奋了?快要炸开了?太天真了。
由数据范围(0n1018)x的值可能很大,所以组合数不可以直接预处理,怎么办?因为len是从1到m枚举的,所以当len=1时,组合数的值为1,于是我们可以一个一个转移组合数的值。
当我们从Clen1x+len1转移到Clenx+len时,实际上,Clenx+len=Clen1x+len1×x+lenlen
因为有模,所以要用逆元,所以
Clenx+lenClen1x+len1×(x+len)×lenp2(modp)
于是就可以完美解决了,除预处理外时间复杂度O(m3)

下面附一下代码

#include<iostream>#include<cstdio>#include<algorithm>#include<cmath>#include<numeric>#include<cstring>#include<queue>#include<functional>#include<set>#include<map>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fd(i,a,b) for(int i=a;i>=b;i--)typedef long long LL;const int mod = 905229641;const int M = 110;using namespace std;LL n;LL m,f[M][M*M],ans,js[M];void prepare(){    f[0][0]=1;    fo(i,0,m-1)        fd(x,i,0)            fo(y,0,m*(m-1)/2-i)            if (f[x][y])f[x+1][y+i]=(f[x][y]+f[x+1][y+i])%mod;    js[1]=1;    fo(i,2,m)js[i]=js[i-1]*i%mod;}LL quickmi(LL x,int tim){    LL ans=1;    while (tim){        if (tim%2==1)ans=(ans*x)%mod;        x=(x*x)%mod;        tim/=2;    }    return ans;}void getans(){    ans=0;    fo(i,0,m*(m-1)/2)    if ((n-i)%m==0){        LL x=((n-i)/m)%mod;        LL tot=1;        fo(j,1,m){            ans=(ans+f[j][i]*js[j]%mod*tot%mod)%mod;            tot=(tot*(x+j)%mod*quickmi(j,mod-2)%mod)%mod;        }       }}int main(){    scanf("%lld%d",&n,&m);    prepare();    getans();    printf("%lld\n",ans);}
0 0
原创粉丝点击