洛谷·[JXOI2018]游戏

初见安~这里是传送门:洛谷P4562 游戏

题目背景

九条可怜是一个富有的女孩子。

题目描述

她长大以后创业了,开了一个公司。 但是管理公司是一个很累人的活,员工们经常背着可怜偷懒,可怜需要时不时对办公室进行检查。

可怜公司有 nn 个办公室,办公室编号是 l到 l+n-1,可怜会事先制定一个顺序,按照这个顺序依次检查办公室。一开始的时候,所有办公室的员工都在偷懒,当她检查完编号是 ii 的办公室时候,这个办公室的员工会认真工作,并且这个办公室的员工通知所有办公室编号是 ii 的倍数的办公室,通知他们老板来了,让他们认真工作。因此,可怜检查完第 ii 个办公室的时候,所有编号是 ii 的倍数(包括 ii )的办公室的员工会认真工作。

可怜发现了员工们通风报信的行为,她发现,对于每种不同的顺序 pp ,都存在一个最小的 t(p),使得可怜按照这个顺序检查完前 t(p) 个办公室之后,所有的办公室都会开始认真工作。她把这个 t(p)定义为 p 的检查时间。

可怜想知道所有 t(p)t的和。

但是这个结果可能很大,她想知道和对 10^9+7109+7 取模后的结果。

输入格式

第一行输入两个整数 l,rl,r 表示编号范围,题目中的 nn 就是 r-l+1r−l+1 。

输出格式

一个整数,表示期望进行的轮数。

输入 

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 。

和是 1616 。

数据范围

对于 20% 的数据,r-l+1\leq 8
对于另 10% 的数据,l=1
对于另 10% 的数据,l=2
对于另 30% 的数据,l\leq 200
对于 100% 的数据,1\leq l\leq r\leq 10^7

题解

这个题的题意很奇怪啊……明明是求的所有情况耗时之和,也就是检查的数量之和,输出解释写的又成了轮数的期望了……不管,我们求的是和不是期望。

题意转化过来就是:问你在所有的检查顺序下,哪些房间是没有被前面的因数通报的。再换句话说,有些房间是关键的房间,不会被别的房间通报,也就是说没有数以它作为倍数,除了它自己。所以说你每次检查要花费的时间至少就是关键房间的数量,至多是所有房间的数量

关键房间的数量很好求,假设有sum个,暴力筛就行了。接下来考虑答案怎么算——我们设f[i]表示恰好花费时间i检查完所有关键房间的情况数。那么就有:

ans = \sum_{i = sum} ^ n i * f[i]

再看怎么算f[i]。因为已经检查了i个房间了,也就是说还有n-i个房间没有检查,而这n-i个房间都不是关键房间,我们随便选n-i个不关键的再任意顺序即可,也就是有C _{n-sum}^{n-i} * (n-i)!种情况;再看选了的i种里面,因为只需要i的时间,所以第i次一定是选的关键房间中的一个,而前面的i-1次已经因为之前n-i个的组合数枚举固定了是哪i-1个房间,所以我们只考虑顺序,就有:sum*(i-1)!。所以:

f[i]=C_{n-sum}^{n-i}*(n-i)!*sum*(i-1)!

公式已经有了,具体操作略。

上代码——

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
const int maxn = 1e7 + 4;
const int p = 1e9 + 7;
typedef long long ll;
int read() {
	int x = 0, f = 1, ch = getchar();
	while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
	return x * f;
}


ll fac[maxn], inv[maxn], f[maxn], ans = 0;

ll C(ll n, ll m) {
	if(!m) return 1;
	return fac[n] * inv[m] % p * inv[n - m] % p;
}

ll power(ll a, ll b) {ll res = 1; while(b) {if(b & 1) res = (res * a) % p; a = a * a % p, b >>= 1;}return res;}

int l, r, n, sum = 0;
bool vis[maxn];
signed main() {
	l = read(), r = read(); n = r - l + 1;
	for(int i = l; i <= r; i++) if(!vis[i]) {
		sum++;
		for(int j = (i << 1); j <= r; j += i) vis[j] = true;//暴力筛
	}
	
	fac[1] = fac[0] = 1;
	for(int i = 2; i <= n; i++) fac[i] = fac[i - 1] * i % p;//预处理阶乘
	
	inv[n] = power(fac[n], p - 2); inv[0] = 1;//预处理逆元
	for(int i = n - 1; i > 0; i--) inv[i] = inv[i + 1] * 1ll * (i + 1) % p;
	for(int i = sum; i <= n; i++) 
        f[i] = C(n - sum, n - i) * fac[n - i] % p * sum % p * fac[i - 1] % p;
		
	for(int i = sum; i <= n; i++) ans = (ans + i * f[i] % p) % p;
	printf("%lld\n", ans);
	return 0;
}

迎评:)
——End——

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值