1. 程式人生 > >[六省聯考2017]分手是祝願 - 題解

[六省聯考2017]分手是祝願 - 題解

題目連結
做法:
首先預處理出每個數的約數,用 $ vector $ 存,時間是調和級數 $ O(n \log n) $ 。
部分分:當 $ n = k $ 時,每次操作最優,然後從右往左列舉,若果當前為1則用掉一次,暴力修改。得 $ 50 $ 分。
正解: $ f[i] $ 表示對於 $ n $ 盞燈,從需要按 $ i $ 次能全部熄滅到按 $ i - 1 $ 次能全部熄滅的期望。得

f[i] = i * inv[n] + (n - i) * inv[n] * (f[i] + f[i+1] + 1)

移項得

f[i] = (f[i+1] * (n - i) + n) * inv[i];

從 $ n $ 倒推。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=100003;
const int N=100010;
int n,k,tot;
int a[N];
vector <int> d[N];
ll f[N],inv[N],ans=0;

void init() {
    for(int i=1;i<=n;i++) for(int j=i;j<=n;j+=i) d[j].push_back(i);
}
int main() {
    scanf("%d%d",&n,&k),init();
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    inv[0]=inv[1]=1;
    for(int i=2;i<=n;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    for(int i=n;i>=1;i--)
        if(a[i]) {
            ++tot; for(int j=0;j<d[i].size();j++) a[d[i][j]]^=1;
        }
    f[n]=1;
    for(int i=n-1;i>k;i--) f[i]=(n%mod+(n-i)%mod*f[i+1]%mod)%mod*inv[i]%mod;
    for(int i=k;i;i--) f[i]=1;
    for(int i=1;i<=tot;i++) ans=(ans+f[i])%mod;
    for(int i=2;i<=n;i++) ans=ans*i%mod;
    printf("%lld\n",ans);
    return 0;
}