「JXOI2018」游戏
题意
可怜公司有 \(n\) 个办公室,办公室编号是 \(l\sim l+n-1\) ,可怜会事先制定一个顺序,按照这个顺序依次检查办公室。一开始的时候,所有办公室的员工都在偷懒,当她检查完编号是 \(i\) 的办公室时候,这个办公室的员工会认真工作,并且这个办公室的员工通知所有办公室编号是 \(i\) 的倍数的办公室,通知他们老板来了,让他们认真工作。因此,可怜检查完第 \(i\) 个办公室的时候,所有编号是 \(i\) 的倍数(包括 \(i\) )的办公室的员工会认真工作。
她发现,对于每种不同的顺序 \(p\) ,都存在一个最小的 \(t(p)\) ,使得可怜按照这个顺序检查完前 \(t(p)\) 个办公室之后,所有的办公室都会开始认真工作。她把这个 \(t(p)\) 定义为 \(p\) 的检查时间。
可怜想知道所有 \(t(p)\) 的和对 \(10^9+7\) 取模后的结果。
\(l, n \le 10^7\)
题解
比较舒服的签到题。
我们定义 \([l, r]\) 中的神仙数 \(p\) ,当且仅当 \(p\) 除了 \(p\) 外的任意一个因子都不存在于 \([l, r]\) 中。
这个显然我们可以用线性筛预处理,做到 \(O(n)\) 的复杂度。其实也可以通过埃筛做到 \(O(n \ln \ln n)\) 的复杂度。
假设 \([l, r]\) 中的神仙数共有 \(tot\) 个,那么就意味着只要这 \(tot\) 个数全部遍历过就可以结束了,反之不能结束。
这是很好证明的,因为这些数不遍历的话,那么不存在别的数能把他们消掉。反之这些数遍历了,别的数都能通过这些数消掉。
那么枚举在第几个数便利完,那么贡献就很好计算了:
\[
\sum_{i= tot}^{n} {i - 1 \choose tot - 1} \times (tot - 1)! \times (n - tot)! \times i
\]
假设在 \(i\) 处结束,那么第 \(i\) 个必为神仙数,那么就是在前 \(i - 1\) 个位置填 \(tot - 1\) 的排列,然后其他数可以随意安排,注意不要漏乘此处的贡献是 \(i\) 。
然后线性预处理逆元就可以把复杂度做到 \(O(n)\) 了。
代码
#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
inline int read() {
int x = 0, fh = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar() ) if (ch == '-') fh = -1;
for (; isdigit(ch); ch = getchar() ) x = (x << 1) + (x << 3) + (ch ^ 48);
return x * fh;
}
void File() {
#ifdef zjp_shadow
freopen ("2544.in", "r", stdin);
freopen ("2544.out", "w", stdout);
#endif
}
typedef long long ll;
const int N = 1e7 + 1e3, Mod = 1e9 + 7;
ll fpm(ll x, int power) {
ll res = 1;
for (; power; power >>= 1, (x *= x) %= Mod)
if (power & 1) (res *= x) %= Mod;
return res;
}
int l, r, n; ll fac[N], ifac[N];
inline int C(int n, int m) {
if (n < 0 || m < 0 || m > n) return 0;
return 1ll * fac[n] * ifac[m] % Mod * ifac[n - m] % Mod;
}
void Init(int maxn) {
fac[0] = ifac[0] = 1;
For (i, 1, maxn) fac[i] = 1ll * fac[i - 1] * i % Mod;
ifac[maxn] = fpm(fac[maxn], Mod - 2);
Fordown (i, maxn - 1, 1)
ifac[i] = 1ll * ifac[i + 1] * (i + 1) % Mod;
}
bitset<N> vis;
int main () {
File();
l = read(); r = read(); n = r - l + 1;
Init(1e7);
int tot = 0, ans = 0;
For (i, l, r) if (!vis[i]) {
++ tot; for (int j = i; j <= r; j += i) vis[j] = true;
}
For (i, tot, n)
(ans += 1ll * C(i - 1, tot - 1) * fac[tot] % Mod * fac[n - tot] % Mod * i % Mod) %= Mod;
cout << ans << endl;
return 0;
}
后面两题先咕着,似乎不好做。。。
原文地址:https://www.cnblogs.com/zjp-shadow/p/9978542.html