1. 程式人生 > >JXOI 2018 簡要題解

JXOI 2018 簡要題解

pro 編號 memset fin def bug esp names isdigit

「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;

}

後面兩題先咕著,似乎不好做。。。

JXOI 2018 簡要題解