1. 程式人生 > >【BZOJ4872】分手是祝願(動態規劃,數學期望)

【BZOJ4872】分手是祝願(動態規劃,數學期望)

esp math map ostream pac mes ++i rac define

【BZOJ4872】分手是祝願(動態規劃,數學期望)

題面

BZOJ

題解

對於一個狀態,如何求解當前的最短步數?
從大到小枚舉,每次把最大的沒有關掉的燈關掉
暴力枚舉因數關就好

假設我們知道了當前至少要關\(tot\)
如果一個燈被動兩次以上是沒有任何意義的
所以,相當於,要動的燈只有\(tot\)
其他的是沒有任何意義的
所以,題面可以變為:
現在有\(tot\)\(1\)\(n-tot\)\(0\)
每次隨機選擇一個數將其異或\(1\)
求最終變為\(0\)的期望

我們現在考慮一下
\(f[x]\)為剩下\(x\)\(1\)的期望
並且我們知道了所有的值,
那麽,我們不難推出:

\[f[x]=\frac{x}{n}(f[x-1]+1)+\frac{n-x}{n}(f[x+1]+1)\]
也就是
\[f[x]=\frac{x}{n}f[x-1]+\frac{n-x}{n}f[x+1]+1\]

同時,我們有邊界:
\(f[x]=x(x\leq K)\)
\(f[n]=f[n-1]+1\)
如果考慮把\(f[n]\)帶入到\(f[n-1]\)的式子中
我們可以得到只有\(f[n-1],f[n-2]\)之間的關系式
如此遞推下去就可以推出\(f[K+1]\)\(f[K]\)的關系式
這樣就是常數項了
回朔帶回去就可以求解
時間復雜度\(O(nlogn)\)

#include<iostream>
#include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<set> #include<map> #include<vector> #include<queue> using namespace std; #define ll long long #define RG register #define MAX 120000 #define MOD 100003 inline
int read() { RG int x=0,t=1;RG char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=-1,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*t; } int fpow(int a,int b) { int s=1; while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;} return s; } int tot,n,K; int a[MAX],ans[MAX]; int inv[MAX]; int DFS(int x,int ss) { if(x<=K)return ans[x]=x; ss=(1ll*n*inv[x]%MOD+1ll*ss*(n-x)%MOD*inv[x]%MOD)%MOD; return ans[x]=(DFS(x-1,ss)+ss)%MOD; } 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]) { for(int j=1;j*j<=i;++j) if(i%j==0) { a[j]^=1; if(j*j!=i)a[i/j]^=1; } ++tot; } if(tot<=K) { for(int i=1;i<=n;++i)tot=1ll*tot*i%MOD; printf("%d\n",tot); return 0; } for(int i=1;i<=n;++i)inv[i]=fpow(i,MOD-2); DFS(n,1); for(int i=1;i<=n;++i)ans[tot]=1ll*ans[tot]*i%MOD; printf("%d\n",ans[tot]); return 0; }

【BZOJ4872】分手是祝願(動態規劃,數學期望)