BZOJ5323 JXOI2018 游戏

传送门

这是我见过的为数不多的良心九怜题之一。

题目大意

有一堆屋子,编号为$l,l+1...r-1,r$,你每次会走入一个没走入过的房子,然后这个房子以及编号为这个房子编号的倍数的房子就会被自动标记,对于每一种走入房子顺序的排列,对答案的贡献是最早使得所有房子都被标记的操作数,求所有排列对答案的贡献和。$1leq l,rleq 10^7$

题解

设$n=r-l+1$不难发现,有意义的走入只有$m$次($m$表示$[l,r]$内没有因数$in[l,r]$的数的数量)。

每种排列对答案的贡献是这$m$个中最后一个被就走入的操作。

枚举最后一个被走入的时间$k$,则需要在前$k-1$个操作中安排$m-1$个位置,由于$m$个有意义操作和$n-m$个无意义操作内部的顺序是无所谓的,所以答案就是$$m!(n-m)!sumlimits_{k=m}^{n}dbinom{k-1}{m-1}$$。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define mod 1000000007
#define M 10000020
using namespace std;
int read(){
	int nm=0,fh=1; char cw=getchar();
	for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
	return nm*fh;
}
int add(int x,int y){return (x+y>=mod)?x+y-mod:x+y;}
int mul(int x,int y){return (LL)x*(LL)y%mod;}
int L,R,n,m,fac[M],ifac[M],ans; bool vis[M];
int qpow(int x,int sq){
	int res=1;
	for(;sq;sq>>=1,x=mul(x,x)) if(sq&1) res=mul(res,x);
	return res;
}
int C(int tot,int tk){return mul(fac[tot],mul(ifac[tk],ifac[tot-tk]));}
int main(){
	L=read(),R=read(),n=R-L+1,fac[0]=1;
	for(int i=1;i<=n;i++) fac[i]=mul(fac[i-1],i); ifac[n]=qpow(fac[n],mod-2);
	for(int i=n;i>0;i--) ifac[i-1]=mul(ifac[i],i);
	for(int i=L;i<=R;i++){
		if(vis[i]) continue; m++;
		for(int j=(i<<1);j<=R;j+=i) vis[j]=true;
	}
	for(int i=m;i<=n;i++) ans=add(ans,mul(i,C(i-1,m-1)));
	printf("%d
",mul(mul(fac[m],fac[n-m]),ans)); return 0;
}
原文地址:https://www.cnblogs.com/OYJason/p/9821462.html