[JXOI2018]游戏

[JXOI2018]游戏

有n个元素,标号l~r(r-l+1=n),进行全排列,从左至右标记,同时对于数i被标记还会去标记其他的标号为其倍数的元素,记最小的标号,标记完所有的元素为权值,问所有排列方案权值之和为多少\(mod\ 10^9+7\)\(1≤l≤r≤10^7\)

显然标记方法模拟极其复杂,于是寻求状态量,即发现有一些最小的数满足只有它自己能够标记它自己,这些数时必须要选的,不妨把它叫做最基本的数,于是是否标记完,即看它们是否选完,实际上,包括一些tarjan的题目也是看最关键的元素,这是一种常见的思想。

所以不难知道我们可以利用埃式筛或者欧拉筛,筛出这些最基本的数,权值即最右边的最基本的数的位置,所以需要枚举这个位置i,其次还要枚举这个位置选哪个数,再把剩下的数排列一下,即有(设最基本的数个数为k)

\[ans=\sum_{i=k}^nk\times P_{i-1}^{k-1}\times(n-k)!=\]
\[\sum_{i=k}^nk\times \frac{(n-k)!(i-1)!}{(i-k+1)!}\]

预处理出阶乘及其逆元,套用公式暴力枚举即可。

参考代码:

#include <iostream>
#include <cstdio>
#define il inline
#define ri register
#define yyb 1000000007
#define gzy 1000000005
#define ll long long
using namespace std;
bool vis[10000001];
int tot,jc[10000001],jv[10000001];
il int pow(int,int);
il void sieve(int,int);
int main(){
    int l,r,i,ans(0);
    scanf("%d%d",&l,&r),sieve(l,r),r-=l,++r;
    for(i=jc[0]=1;i<=r;++i)jc[i]=(ll)jc[i-1]*i%yyb;
    jv[r]=pow(jc[r],gzy),jv[0]=1;
    for(i=r;i>1;--i)jv[i-1]=(ll)jv[i]*i%yyb;
    for(i=tot;i<=r;++i)
        (ans+=(ll)tot*jc[r-tot]%yyb*jc[i-1]%yyb*jv[i-tot]%yyb*i%yyb)%=yyb;
    printf("%d",ans);
    return 0;
}
il int pow(int x,int y){
    int ans(1);
    while(y){
        if(y&1)ans=(ll)ans*x%yyb;
        x=(ll)x*x%yyb,y>>=1;
    }return ans;
}
il void sieve(int l,int r){
    int i;
    while(l<=r){
        if(!vis[l])
            for(++tot,i=l<<1;i<=r;i+=l)
                vis[i]|=true;
        ++l;
    }
}

转载于:https://www.cnblogs.com/a1b3c7d9/p/10795556.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值