【XSY2576】【2017国家集训队】换教室

\(Description\)

对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程。

在可以选择的课程中,有 \(2n\) 节课程安排在 \(n\) 个时间段上。在第 \(i(1≤i≤n)\)个时间段上,两节内容相同的课程同时在不同的地点进行,其中,牛牛预先被安排在教室 \(c_{i}\) 上课,而另一节课程在教室 \(d_{i}\) 进行。

在不提交任何申请的情况下,学生们需要按时间段的顺序依次完成所有的 \(n\) 节安排好的课程。如果学生想更换第 \(i\) 节课程的教室,则需要提出申请。若申请通过,学生就可以在第 \(i\) 个时间段去教室 \(d_{i}\) 上课,否则仍然在教室 \(c_{i}\) 上课。

由于更换教室的需求太多,申请不一定能获得通过。通过计算,牛牛发现申请更换第 \(i\) 节课程的教室时,申请被通过的概率是一个已知的实数 \(k_{i}\),并且对于不同课程的申请,被通过的概率是互相独立的。

学校规定,所有的申请只能在学期开始前一次性提交,并且每个人只能选择至多 \(m\) 节课程进行申请。这意味着牛牛必须一次性决定是否申请更换每节课的教室,而不能根据某些课程的申请结果来决定其他课程是否申请;牛牛可以申请自己最希望更换教室的 \(m\) 门课程,也可以不用完这 \(m\) 个申请的机会,甚至可以一门课程都不申请。


(真正的题目才刚刚开始,上面的都是浮云)

因为不同的课程可能会被安排在不同的教室进行,所以牛牛需要利用课间时间从一间教室赶到另一间教室。某天,当牛牛上完课来到下一间教室时,发现教室里空无一人,黑板上用白粉笔写着\(N\)个数字\(0\)\(M\)个数字\(1\),旁边贴着一个纸条,纸条上写着另一个整数\(K\)

牛牛观察了一会儿,发现\(K−1\)竟然整除\(N+M−1。\)于是,好奇的牛牛想知道,如果称“在黑板上随机选择\(K\)个数擦掉,并把这\(K\)个数的平均数作为一个新的数写在黑板上”为一次操作,在他一直重复操作直到黑板上只剩一个数后,这个数有多少种可能的取值。显然,在这个过程中,黑板上的每个数都是有理数,牛牛是大学生,学过关于分数的知识,所以你不用关心他怎么如何把一个有理数写在黑板上。请你能帮助他统计这个数值,由于该数值过大,你只需要求出它对\(10^9+7\)取模的值就行了。


\(Input\)

输入第一行包含三个整数\(N,M,K\)


\(Output\)

输出一个整数,表示黑板上最后的数字可能的取值数量对\(10^9+7\)取模的值。


\(Sample Input 1\)

2 2 2

扫描二维码关注公众号,回复: 7279097 查看本文章

\(Sample Input 2\)

3 4 3

\(Sample Input 3\)

150 150 14


\(Sample Output 1\)

5

\(Sample Output 2\)

9

\(Sample Output 3\)

937426930


\(HINT\)

在第一个样例中,\(5\)个可能的取值分别是\(\frac{1}{4}\)\(\frac{3}{8}\)\(\frac{1}{2}\)\(\frac{5}{8}\)\(\frac{3}{4}\)。其中,\(\frac{3}{8}\)可以通过以下方式得到:

  1. 擦去\(0\)\(1\),写上\(\frac{1}{2}\)
  2. 擦去\(\frac{1}{2}\)\(1\),写上\(\frac{3}{4}\)
  3. 擦去\(0\)\(\frac{3}{4}\),写上\(\frac{3}{8}\)

数据范围:

\(1≤N,M≤2000,\)
\(2≤K≤2000,\)
\(K−1\)整除\(N+M−1\)

存在10分的子任务,满足\(1≤N,M,K≤10。\)
存在30分的子任务(独立于上一个子任务),满足\(K=2。\)


吐槽

有没有看到标题和体面很激动的小盆友,没错,我跟你们一样:这不就是\(2016\)\(tg\)的原题吗?!!!

但这次终于见识到一句话入主题的精髓了

那个改题面的人,站出来,保证不盘死你

TMD之前的题面跟题目有什么关系啊啊


思路

按照习惯,看到小学生最擅长的数数题,并且需要取模,我们首先考虑\(dp\)

我们观察每次取\(k\)个数的平均数,是不是很像一棵\(k\)叉树?!

于是我们考虑一棵\(k\)叉树,它的叶子节点为\(0\)\(1\),每个非叶子节点的值就是它的儿子的平均值

于是我们可以得到:这棵\(k\)叉树有\(n+m\)个叶子节点,\(n\)\(0\)\(m\)\(1\)

\(n\)\(0\)叶子节点的深度分别为\(d0_{i}\)\(m\)\(1\)叶子节点的深度分别为\(d1_{i}\)

那么我们容易得到根节点的值就为\(\sum k^{-d1_{i}}\)

我们考虑,当全部叶子节点的值都为\(1\)时,那么根节点的值就为\(1\)

于是,我们可以得到

\(\sum k^{-d1_{i}}+\sum k^{-d0_{i}}=1\)

现在,我们所要求的东西就转换成了:

有多少个\(x\)可以写成\(\sum k^{-d1_{i}}\)的形式,且\(1-x\)可以写成\(\sum k^{-d0_{i}}\)的形式(虽然我但代码里求的是\(1-x\)的个数,但都一样)

我们发现这些\(\sum\)很烦人,于是我们将\(x\)表示成一个\(k\)进制小数 \(0.a_{1}a_{2}a_{3}...a_{len}\),这个小数的长度为\(len\)

因为这小数部分都是由叶子节点为\(1\)的值构成的,于是\(x\)的每位小数的和为\(\sum_{i=1}^{len}a_{i}=m\),当然,这是在没有进位之前的情况。一旦在第\(p\)位进位,那么\(a_{p}-=1,a_{p+1}+=k\),令\(val=\sum_{i=1}^{len}a_{i}\)
所以可以得到 \(val\equiv m(mod\) \(k+1)\)

我们再来看\(1-x\),同理,\(1-x\)的长度也一定是\(len\),并且它的位数和可以表示为\((len-1)(k-1)+k-val =len(k-1)-val+1\),易得\(len(k-1)-val+1\)需要满足\(<=n\)

我们的结论已经推完啦,接下来就是\(dp\)

我们设\(dp[i][j]\)表示到小数点后第\(i\)位,之前的位数和为\(j\)的方案数

又因为小数不能有后缀\(0\),所以我们要加一个状态\(k\)表示是否为\(0\)

我们在\(dp\)时可以考虑用前缀和\(sum\)优化

\(p.s:\)记得开\(long\) \(long\)


代码:

#include<bits/stdc++.h>
using namespace std;
const long long N=2010,mod=1e9+7;
long long sum[N];
long long f[N<<1][N][2];
long long n,m,k;
int main()
{
    scanf("%lld %lld %lld",&n,&m,&k);
    f[0][0][0]=1;
    long long ans=0ll;
    for(long long i=1;i<=max(n,m)*2;i++)
    {
        sum[0]=(f[i-1][0][0]+f[i-1][0][1])%mod;
        for(long long j=1;j<=n;j++)
            sum[j]=(sum[j-1]+(f[i-1][j][0]+f[i-1][j][1])%mod)%mod;
        for(long long j=0;j<=n;j++)
        {
            f[i][j][0]=(sum[j]-sum[j-1])%mod;
            if(j)
            {
                if(j>=k)f[i][j][1]=(sum[j-1]-sum[j-k]+mod)%mod;
                else f[i][j][1]=(sum[j-1]+mod)%mod;
            }
        }
        for(long long j=0;j<=n;j++)
        {
            if(j%(k-1)==n%(k-1)&&(i*(k-1)-j+1)%(k-1)==m%(k-1)&&i*(k-1)-j+1<=m)ans=(ans+f[i][j][1])%mod;
        }
    }
    printf("%lld",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ShuraEye/p/11519366.html
今日推荐