upc 训练赛第六场 游戏(组合数)

问题 B: 游戏

时间限制: 1 Sec  内存限制: 512 MB
提交: 180  解决: 48
[提交] [状态] [讨论版] [命题人:admin]

题目描述

九条可怜是一个热爱游戏的女孩子,她经常在网上和一些网友们玩一款叫做《僵尸危机》游戏。
在这款游戏中,玩家们会需要在成为僵尸之前与黑恶势力斗智斗勇,逃离被病毒感染的小岛。但是黑恶势力不会让玩家轻易得逞,他会把一些玩家抓走改造成僵尸。变成僵尸的玩家会攻击其他的玩家,被攻击的玩家会被”感染”,成为病毒的潜在宿主。
具体来说,游戏开始时,所有的玩家会获得一个L∼R的编号(如果一共有R−L+1个玩家),不同的玩家的编号不同。
游戏分轮次进行,在每一轮中一次会发生这样的事情。
•如果所有当前所有的正常人都已经被感染,那么游戏结束。
•不然,黑恶势力会在当前的正常人(包括被感染的人)中等概率随机一个改造成僵尸。
•被改造成僵尸的玩家会攻击所有编号是他的倍数的玩家,使得他们被感染。
九条可怜现在想知道,这个游戏期望会进行多少轮?这个答案可能是一个实数,她想让你给出期望轮数乘上(R−L+1)!以后的结果,这个结果可能很大,请对109+7取模后输出。

输入

第一行输入两个整数 L, R 表示编号范围。

输出

一个整数,表示期望进行的轮数。

样例输入

2 4

样例输出

16

提示

• 2 3 4, 轮数是 2。
• 3 2 4, 轮数是 2。
• 4 2 3, 轮数是 3。
• 4 3 2, 轮数是 3。
• 2 4 3, 轮数是 3。
• 3 4 2, 轮数是 3。
每种情况的概率都是 1/6,于是期望轮数就是 (2 + 2 + 3 + 3 + 3 + 3)/6 =8/3。
乘上 3! = 6 以后就是 16 。

对于 20% 的数据,R − L + 1 ≤ 8。
对于另 10% 的数据,L = 1。
对于另 10% 的数据,L = 2。
对于另 30% 的数据,L ≤ 200。
对于 100% 的数据,1 ≤ L ≤ R ≤ 107 。

题解: 

1.首先要求得筛掉所有的数所需要最少的数的个数(即只要在剩下那些数前边拿这些数就可以减少拿的次数)

2。假设必须取的数为sum个,对于本题来说就是游戏的轮数应当介于sum到r-l+1

3.我们设s[i]为游戏轮数为i时的排列有多少种.

4.轮数为i那么第i位一定时sum个数中的一个(情况有sum种(C(sum,1))并且剩余的sum-1个数在第i位前边。

5.第i位前边有i-1个数 有sum-1个数必选的 剩余的i-1-(sum-1)是从剩余的n-sum个数中选的

情况为C(n-sum,i-sum)乘前i-1个数的全排列,乘后n-i个数的全排列

6.综上所述公式为sigama(i从sum到r-l+1)s[i]=sum*(C(n-sum,i-sum))*(i-1)!*(n-i)!,因为这个是轮数为i的排列个数,所以还要乘i

7.还有就是注意那个O(n)求阶乘逆元的方法(学习了)。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define LL long long
#define N 10000005
const LL mod = 1000000007;
LL fac[N];
LL ni[N];
int vis[N];
inline LL qpow(LL a,LL k)
{
    LL tm=1;
    while(k)
    {
        if(k&1)
            tm=(tm*a)%mod;
        a=(a*a)%mod;
        k>>=1;
    }
    return tm%mod;
}
int main()
{
   int l,r;
   scanf("%d%d",&l,&r);
   fac[0]=1;
   for(register int i=1;i<=r;++i)fac[i]=(i*fac[i-1])%mod;
   ni[r]=qpow(fac[r],mod-2);
   for(register int i=r;i;--i)ni[i-1]=(i*ni[i])%mod;
   LL sum=0;
   for(register int i=l;i<=r;++i)
   {
       if(!vis[i])
       {
           sum++;
           for(register int j=1;j*i<=r;++j){
                vis[j*i]=1;
            }
       }
   }
   LL ans=0;
   LL n=r-l+1;
   for(register int i=sum;i<=r-l+1;++i)
   {
       ans=(ans+sum*fac[n-sum]%mod*ni[i-sum]%mod*fac[i]%mod)%mod;
   }
   printf("%lld\n",ans);
}

参考博客:https://www.luogu.org/problemnew/solution/P4562

猜你喜欢

转载自blog.csdn.net/weixin_40894017/article/details/81297651