游戏

6887: 游戏

时间限制: 1 Sec  内存限制: 512 MB
提交: 228  解决: 64
[提交] [状态] [讨论版] [命题人: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 。

分析:题目让求的期望是假设共出现ans次,那么期望是ans/(排列所有情况的次数)*(r-l+1)!(阶乘),一算发现排列所有情况就是r-l+1的阶乘,其实就是求所有次数的和根本不是期望。
 

1、首先我们要求取走所有数所需要几个数。设为sum。

2、游戏的轮数就一定在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、l==1时要特判一下,因为选了1之后所有都被感染,那么就枚举1出现的位置。1*(r-1)!+2*(r-1)!+......4*(r-1)! 将(r-1)!提出来后,得到公式:r!*(n+1)/2。

#include<stdio.h>
#include<algorithm>
const int MAX=1e7+10;
const int mod=1e9+7;
typedef long long ll;
using namespace std;
inline ll qpow(ll a,ll b){ll r=1,t=a; while(b){if(b&1)r=(r*t)%mod;b>>=1;t=(t*t)%mod;}return r;}
bool vis[MAX];
ll fac[MAX];
ll inv[MAX];
ll l,r,n;
void init()
{
    fac[0]=1;
    for(int i=1;i<=r;i++)
        fac[i]=1ll*i*fac[i-1]%mod;

    inv[r]=qpow(fac[r],mod-2);

    for(int i=r;i;i--)
        inv[i-1]=1ll*i*inv[i]%mod;
}

int main()
{
    scanf("%lld%lld",&l,&r);
    init();
    if(l==1)
    {
        printf("%lld\n",1ll*(fac[r]*(r+1)%mod*qpow(2,mod-2)%mod)%mod);
        return 0;
    }

    int sum=0;
    for(int i=l;i<=r;i++)
    {
        if(!vis[i])
        {
            sum++;
            for(ll j=i<<1;j<=r;j+=i)
                vis[j]=1;
        }
    }

    n=r-l+1;
    ll ans=0;
    for(int i=sum;i<=n;i++)
        ans=(ans+1ll*sum*fac[n-sum]%mod*inv[i-sum]%mod*fac[i]%mod)%mod;
    printf("%lld\n",(ans+mod)%mod);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值