P3750 [六省联考2017]分手是祝愿(期望DP+质因数分解)

洛谷题目传送门
先从这个题跳出来,思考如何用最少的次数将灯关掉
因为一个灯能控制的灯都小于他,所以最右边的灯一定要按,因为如果按他的倍数就更不优,所以就从高到底扫一遍,遇到亮的灯就点一次,因为每个灯都是不可替代的,所以这启示我们这些灯在方案中是必须要按的
求出这些灯的个数 c n t cnt cnt,也就是最少要按的次数,如果cnt小于等于k,则直接输出cnt即可
所以设 f [ i ] f[i] f[i]表示将剩下 i i i盏必须要按的灯变成(i-1)盏必须要按的灯的期望次数
f [ i ] = i n × 1 + n − i n × ( f [ i ] + f [ i + 1 ] + 1 ) f[i]=\frac{i}{n}\times 1+\frac{n-i}{n}\times (f[i]+f[i+1]+1) f[i]=ni×1+nni×(f[i]+f[i+1]+1)
改式的意义为,有 i n \frac{i}{n} ni的概率按对,也就是将 i i i盏必须要按的灯变为 i − 1 i-1 i1盏需要按的灯,有 n − i n \frac{n-i}{n} nni的概率按错,回到 i + 1 i+1 i+1盏灯的情况,然后还需要再按 f [ i ] f[i] f[i]次按回来,最后的1是这一次的贡献
化简得
f [ i ] = 1 i × ( n + ( n − i ) × f [ i + 1 ] ) f[i]=\frac{1}{i}\times (n+(n-i)\times f[i+1]) f[i]=i1×(n+(ni)×f[i+1])
最后答案就是 f [ c n t ] + f [ c n t − 1 ] + … … … … + f [ k + 1 ] + k f[cnt]+f[cnt-1]+…………+f[k+1]+k f[cnt]+f[cnt1]++f[k+1]+k
也就是将cnt盏灯变为cnt-1盏灯,一直变到k盏灯最后按k次按完

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5+7;
const int mod = 1e5+3;
int n,k,cnt=0;
int a[N];
vector<int> fac[N];
LL inv[N],F[N],ans;
LL Pow(LL a,int b)
{
	LL res=1;
	while(b)
	{
		if(b&1) res=res*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return res%mod;
}
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	{
		for(int j=i;j<=n;j+=i)
		fac[j].push_back(i);
	}
	for(int i=n;i>=1;i--)
	{
		if(a[i]==1)
		{
			for(int p=0;p<fac[i].size();p++)
			{
				int j=fac[i][p];
				a[j]^=1;
			}
			cnt++;
		}
	}
	if(cnt<=k) ans=cnt%mod;
	else
	{
		for(int i=1;i<=n;i++)
		inv[i]=Pow(i,mod-2)%mod;
		F[n]=1;
		for(int i=n-1;i>=1;i--)
		F[i]=(i+(n-i)*(1+F[i+1]))*inv[i]%mod;
		for(int i=cnt;i>k;i--)
		ans=(ans+F[i])%mod;
		ans=(ans+k)%mod;		
	}
	for(int i=1;i<=n;i++)
	ans=(ans*i)%mod;
	cout<<ans;
	return 0;
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值