[六省联考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
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/daniel14311531/p/10264112.html