• [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 imes P_{i-1}^{k-1} imes(n-k)!= ]

    [sum_{i=k}^nk imes 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;
        }
    }
    
    
  • 相关阅读:
    Daily Scrum 10.29
    Daily Scrum 10.28
    git第一次commit代码阅读
    软工课程项目-数据库管理
    [Go]字典(map)的操作和约束
    [Go]链表的相关知识
    Kubernetes网络设计原则
    [Go]程序实体
    [Kubernetes]集群配置免密登录Permission denied (publickey,password) 解决办法
    [Go]GOPATH相关知识点
  • 原文地址:https://www.cnblogs.com/a1b3c7d9/p/10795556.html
Copyright © 2020-2023  润新知