[BZOJ5323][JXOI2018]游戏

bzoj
luogu

sol

建立一个图论模型:对于\(x,y\in[l,r]\),若\(x|y\),则连边\(x \to y\)。这样就能连出一个\(DAG\)
那么每选出一个数,被标记出来的数就是这个点在\(DAG\)上能够到达的所有点。
考虑那些入度为零的点组成的集合,一旦选出了这个集合内的所有点就可以达成目标。
所以我们只需要考虑这个集合内的点的选出方案就可以了。
设这些入度为零的点有\(x\)个。
先枚举答案,设选出前\(i\)个数时达到了目标,那么第\(i\)个数就必须是这个集合里的点。
这里的方案数是\(x!\binom{i-1}{x-1}(n-x)!\)
所以答案就是\(\sum_{i=x}^nix!\binom{i-1}{x-1}(n-x)!\)

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 1e7+5;
const int mod = 1e9+7;
int l,r,n,x,zhi[N],pri[N],tot,low[N],inv[N],jc[N],jcn[N],ans;
int main(){
    l=gi();r=gi();n=r-l+1;
    for (int i=2;i<=r;++i){
        if (!zhi[i]) pri[++tot]=i,low[i]=1;
        for (int j=1;i*pri[j]<=r;++j){
            zhi[i*pri[j]]=1;low[i*pri[j]]=i;
            if (i%pri[j]==0) break;
        }
    }
    for (int i=l;i<=r;++i) if (low[i]<l) ++x;
    inv[0]=inv[1]=jc[0]=jcn[0]=1;
    for (int i=2;i<=n;++i) inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
    for (int i=1;i<=n;++i) jc[i]=1ll*jc[i-1]*i%mod,jcn[i]=1ll*jcn[i-1]*inv[i]%mod;
    for (int i=x;i<=n;++i) ans=(ans+1ll*i*jc[i-1]%mod*jcn[i-x]%mod*jc[n-x])%mod;
    printf("%lld\n",1ll*ans*x%mod);return 0;
}

猜你喜欢

转载自www.cnblogs.com/zhoushuyu/p/9484414.html