JXOI 2018 簡要題解
「JXOI2018」遊戲
題意
可憐公司有 \(n\) 個辦公室,辦公室編號是 \(l\sim l+n-1\) ,可憐會事先制定一個順序,按照這個順序依次檢查辦公室。一開始的時候,所有辦公室的員工都在偷懶,當她檢查完編號是 \(i\) 的辦公室時候,這個辦公室的員工會認真工作,並且這個辦公室的員工通知所有辦公室編號是 \(i\) 的倍數的辦公室,通知他們老板來了,讓他們認真工作。因此,可憐檢查完第 \(i\) 個辦公室的時候,所有編號是 \(i\) 的倍數(包括 \(i\) )的辦公室的員工會認真工作。
她發現,對於每種不同的順序 \(p\) ,都存在一個最小的 \(t(p)\) ,使得可憐按照這個順序檢查完前 \(t(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;
}
後面兩題先咕著,似乎不好做。。。
JXOI 2018 簡要題解