[Luogu P4562] [BZOJ 5323] [JXOI2018]游戏

洛谷传送门
BZOJ传送门

题目背景

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

题目描述

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

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

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

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

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

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1:
2 4
输出样例#1:
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 。

和是 16 16 16

数据范围

对于 20% 的数据, r − l + 1 ≤ 8 r-l+1\leq 8 rl+18
对于另 10% 的数据, l = 1 l=1 l=1
对于另 10% 的数据, l = 2 l=2 l=2
对于另 30% 的数据, l ≤ 200 l\leq 200 l200
对于 100% 的数据, 1 ≤ l ≤ r ≤ 1 0 7 1\leq l\leq r\leq 10^7 1lr107

解题分析

显然有一些办公室必须到达一次, 我们用埃氏筛法筛出来这些位置, 然后讨论最靠右的一个关键办公室的位置, 乘上组合数就好了。

总复杂度 O ( n l n ( l n ( n ) ) ) O(nln(ln(n))) O(nln(ln(n)))

代码如下:

#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <string>
#include <iostream>
#include <cstring>
#include <cmath>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 10050000
#define MOD 1000000007ll 
#define ll long long
int tot, lef, rig, n;
bool vis[MX];
int ans, inv[MX];
using namespace std;
void get()
{
	for (R int i = lef; i <= rig; ++i)
	{
		if (!vis[i])
		{
			++tot; 
			for (R int j = i << 1; j <= rig; j += i)
			vis[j] = true;
		}
	}
	inv[0] = inv[1] = 1;
	for (R int i = 2; i <= 1e7; ++i) inv[i] = 1ll * inv[MOD % i] * (MOD - MOD / i) % MOD;
}
int main(void)
{
	int fac, ans;
	scanf("%d%d", &lef, &rig);
	get(); fac = 1; n = rig - lef + 1;
	for (R int i = tot; i >= 2; --i) fac = 1ll * fac * i % MOD;
	ans = fac;
	for (R int mul = tot + 1; mul <= n; ++mul)
	fac = 1ll * fac * mul % MOD * inv[mul - tot] % MOD, ans = (ans + fac) % MOD;
	ans = 1ll * ans * tot % MOD;
	n = n - tot; fac = 1;
	for (R int i = 2; i <= n; ++i) fac = 1ll * fac * i % MOD;
	cout << 1ll * ans * fac % MOD;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值