P3750 [六省联考 2017] 分手是祝愿

P3750\(\mathbf{} \begin{Bmatrix} \frac{{\Large LUOGU-P3750} }{{\color{Red}\Large Solution} }\mathbf{} {No.31} \end{Bmatrix}\times{}\) NeeDna

题目描述

给一个01串,你可以选择点 \(i\) 进行一次操作,每一次操作会导致 \(i\) 的所有约数(包括 \(1\)\(i\))数字改变,也就是与 \(1\) 异或。

现在给你一个数字 \(k\) ,假如你能在 \(k\) 次操作内把序列变成全 \(0\) 的串,那么输出操作数。否则先随机操作任意一个点,直到能在 \(k\) 次操作内把序列变成全 \(0\) 的串后,用最优操作把序列变成全 \(0\) 的串。

输出把序列变成全 \(0\) 的串的期望操作次数,答案乘上 \(n!\),并对100003取模。

数据范围

对于 \(100\%\) 的测试点,\(1 \leq n \leq 100000, 0 \leq k \leq n\)
对于以上每部分测试点,均有一半的数据满足 \(k = n\)

题解

先考虑 \(k=n\) 的时候,其实是在引导我们找到最优操作。我们就先选找一些性质。

  • 操作是可逆的,所以我们每个点只会操作 \(0\) 次和 \(1\) 次,所以最优操作数一定小于等于 \(n\)
  • 操作只会影响不大于自己的点,所以最大的为 \(1\) 的点是必须操作的。
  • 操作的顺序不影响答案。

有了这些性质,我们可以找到一个最优操作了,那就是每次操作最大的为 \(1\) 的点是必须操作的,直到串全部数字为 \(0\)

我们可以把约数建边,暴力操作,求边是\(\Theta(n\log n)\) 的,暴力操作肯定时间复杂度不大于建边的,所以总复杂度是\(\Theta(n\log n)\) 的。

接下来考虑 \(k>ans\) 的情况(\(ans\) 为原本的最优操作)。这其实是一个经典的期望问题,我们把问题转换一下。根据上面的性质,我们发现操作的点只有 \(2\) 类,使剩下最优操作减少使剩下最优操作增加的点。这个时候我们设计dp。设 \(f_i\) 为当前剩下最优操作数为 \(i\),做操作变化到剩下最优操作数为 \(i-1\) 的期望操作数。我们可以得到以下式子:

  • \(\frac{i}{n}\) 的概率按到需要按的灯,此时的期望为 \(\frac{i}{n}\)

  • \(\frac{n-i}{n}\) 的概率按到不需要按的灯,此时的期望为 \(\frac{n-i}{n}\times (f_i+f_{i+1}+1)\)

那么 \(f_i=\frac{i}{n}+\frac{n-i}{n}\times (f_i+f_{i+1}+1)\) 化简一下 \(f_i=\frac{n+(n-i)\times f_{i+1}}{i}\)

这样就可以线性递推了,注意推的时候从 \(n\) 推就可以,因为最优状态下需要的操作次数一定小于等于 \(n\)

code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10,mod=100003;
int n,k,a[N],ans,jie[N],f[N],sum,ni[N];
vector<int> g[N];
int ksm(int x,int k){
	int ans=1;
	while(k>1){
		if(k%2) ans=ans*x%mod;
		x=x*x%mod;k/=2;
	}return x*ans%mod;
}
signed main(){
	cin>>n>>k;
	jie[0]=1;
	for(int i=1;i<=n;i++){
		jie[i]=jie[i-1]*i%mod;
		for(int j=i*2;j<=n;j+=i){
			g[j].push_back(i);
		}
	}
	for(int i=1;i<=n;i++) ni[i]=ksm(i,mod-2);
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=n;i>=1;i--){
		if(a[i]==0) continue;
		ans++;
		for(int v:g[i]){
		    a[v]^=1;
		}
	}
	for(int i=n;i>k;i--){
		f[i]=((n-i)*f[i+1]+n)*ni[i]%mod;
		if(i<=ans) sum=(sum+f[i])%mod;
	}
	if(ans<=k) cout<<ans*jie[n]%mod;
	else cout<<(sum+k)*jie[n]%mod;
	return 0;
}
posted @ 2025-07-25 15:15  NeeDna  阅读(23)  评论(0)    收藏  举报