1. 程式人生 > >[HEOI2017]分手是祝願 期望概率dp 差分

[HEOI2017]分手是祝願 期望概率dp 差分

col 可能 關系 () std include span 遞推 解決

經分析可知:I.操作每個燈可看做一種異或狀態 II.每個狀態可看做是一些異或狀態的異或和,而且每個異或狀態只能由它本身釋放或放入 III.每一種異或狀態只有存在不存在兩中可行狀態,因此這些燈只有同時處於不存在才可以,而兩種異或狀態之間沒有關系因此可以把這些狀態看做一樣的,因此counts的是異或狀態數。

到這裏為止我們可以得到一個簡單的轉移方程 f[i]=i/n*f[i-1]+(n-i)/i*f[i+1]+1 於是看起來似乎已經到了解決問題的時候,所以我就開始推.......然後就沒有然後了,由這個式子出發的扔鍋,永遠沒頭.....

.最後知道正解是差分的我大概......我們可以這樣想,從每個f[i]出發到達最後他一定是先從自己出發再到每個可能第一次到達i-1,在每個可能第一次到達i-2....而我們發現對於一個i到達i-1的期望次數是一定的因此我們可以從此入手 得到 g[i]=i/n+(n-i)/n(g[i+1]+g[i]+1) 這樣我們就能用一個二階遞推來AC了

([email protected]@*) 哇~ 神?差分,讓我推一年我也推不出來.......

#include<cstdio>
#include<iostream>
#define MAXN 100100
using namespace std;
typedef long long LL;
const LL P=100003;
LL jie[MAXN],g[MAXN],f[MAXN],n,k;
int now[MAXN];
inline LL ni(LL x)
{
   LL y=P-2,ans=1;;
   while(y)
   {
     if(y&1
)ans=ans*x%P; y>>=1; x=x*x%P; } return ans; } int main() { scanf("%lld%lld",&n,&k); jie[1]=1; for(LL i=2;i<=n;i++) jie[i]=jie[i-1]*i%P; for(LL i=1;i<=k;i++) g[i]=jie[n]; g[0]=0; g[n]=jie[n]; for(LL i=n-1;i>k;i--) g[i]=((n-i)*g[i+1
]%P+n*jie[n]%P)%P*ni(i)%P; for(int i=1;i<=n;i++) scanf("%d",&now[i]); LL aim=0; for(int i=n;i>0;i--) if(now[i]) { aim++; int j=1; for(;j*j<i;j++) if(i%j==0) now[j]^=1,now[i/j]^=1; if(j*j==i) now[j]^=1; } LL ans=0; for(int i=0;i<=aim;i++) ans+=g[i]; ans%=P; printf("%lld",ans); return 0; }

[HEOI2017]分手是祝願 期望概率dp 差分