1. 程式人生 > >2018.11.01 bzoj4872: [Shoi2017]分手是祝願(期望dp)

2018.11.01 bzoj4872: [Shoi2017]分手是祝願(期望dp)

傳送門
一道不錯的題。


考慮 n = = k n==k 的時候怎麼做。
顯然應該從 n n

1 1 如果燈是開著的就把它關掉這樣是最優的。
不然如果亂關的話會互相影響肯定不如這種優。
於是就可以定義狀態 f [ i ]
f[i]
表示從當前按 i i 盞為最優方案轉移到按 i 1 i-1
盞為最優方案的代價。
然後 f [ i ] = i n + n i n ( 1 + f [ i ] + f [ i + 1 ] ) f[i]=\frac i n+\frac {n-i} n*(1+f[i]+f[i+1])
移項解方程可以推出最後的式子:
f [ i ] = i n ( n + ( n i ) f [ i + 1 ] ) f[i]=\frac i n*(n+(n-i)*f[i+1])
程式碼:

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
typedef long long ll;
const int N=1e5+5,mod=1e5+3;
int n,k,inv[N],a[N],f[N],ans=0,cnt=0;
int main(){
	n=read(),k=read();
	for(int i=1;i<=n;++i)a[i]=read();
	for(int i=n;i;--i)if(a[i]){
		++cnt;
		for(int j=1;j*j<=i;++j){
			if(i!=i/j*j)continue;
			a[j]^=1,a[i/j]^=(i/j!=j);
		}
	}
	if(cnt<=k){
		for(int i=2;i<=n;++i)cnt=(ll)cnt*i%mod;
		cout<<cnt;
		return 0;
	}
	inv[1]=1;
	for(int i=2;i<=n;++i)inv[i]=(ll)inv[mod%i]*(mod-mod/i)%mod;
	f[n]=1;
	for(int i=n-1;i>k;--i)f[i]=(ll)inv[i]*((((ll)n+(ll)(n-i)*f[i+1]%mod))%mod)%mod;
	for(int i=cnt;i>k;--i){
		ans+=f[i];
		if(ans>=mod)ans-=mod;
	}
	ans+=k;
	if(ans>=mod)ans-=mod;
	for(int i=2;i<=n;++i)ans=(ll)ans*i%mod;
	cout<<ans;
	return 0;
}