HDU4624 Endless Spin Min-Max容斥+DP
阿新 • • 發佈:2018-11-08
題目分析
朋友,你聽說過Min-Max容斥嗎?
所謂Min-Max容斥就是這樣一個式子: 。所謂的max,就是集合內最後一個被選中的元素被選中的期望代價,min就是第一個被選中的元素被選中的期望代價。
那麼這題用一個Min-Max容斥,問題就轉化為求每一個集合裡的點,至少選中一個的期望。那麼只要算出每一次決策一個都選不中的概率,也就是選擇一次,選擇的區間不包含這個集合裡任何點的概率就行了。
複雜度
,你只需要把評測機換成神威太湖之光,就能AC此題。
設一個DP狀態, ,表示考慮前 個球是否在集合中,有 個區間可以保證不選到在集合中的球,最後一個選擇的球到 位置之間有 個空位,集合中選擇的球的數量是偶數/奇數的狀態數,轉移通過考慮第 個球選不選有兩種:
這個DP是 的。
假設共有 個球,某個集合 有 個區間可以保證不選到在集合內的球,那麼 ,通過考慮集合內球數量的奇偶性,就可以做Min-Max容斥啦。
但是這題…尼瑪保留15位小數。腦子有坑的litble打了一個高精度小數,並且在打完之後才意識到,應該打一個分數之間的運算,到最後再化成小數,算了,反正過了,就這樣吧。
程式碼
#include<bits/stdc++.h>
using namespace std;
#define RI register int
typedef long long LL;
const int N=52,K=30;
struct QAQ{LL zs,xs[K+3];}ans[N];
LL f[N][N*N][N][2];
int T,n;
void print(QAQ a) {
if(a.xs[16]>=5) ++a.xs[15];
for(RI i=15;i>=2;--i) if(a.xs[i]==10) a.xs[i]=0,++a.xs[i-1];
if(a.xs[1]==10) a.xs[1]=0,++a.zs;
printf("%lld.",a.zs);
for(RI i=1;i<=15;++i) printf("%lld",a.xs[i]);
puts("");
}
QAQ getQAQ(LL fz,LL fm) {
QAQ re; re.zs=fz/fm,fz%=fm,fz*=10;
for(RI i=1;i<=K;++i) re.xs[i]=fz/fm,fz%=fm,fz*=10;
return re;
}
QAQ operator + (const QAQ a,const QAQ b) {
LL x=0;QAQ re;
for(RI i=K;i>=1;--i)
re.xs[i]=a.xs[i]+b.xs[i]+x,x=re.xs[i]/10,re.xs[i]%=10;
re.zs=a.zs+b.zs+x;return re;
}
QAQ operator - (QAQ a,const QAQ b) {
for(RI i=K;i>=2;--i) {
if(a.xs[i]<b.xs[i]) a.xs[i]+=10,--a.xs[i-1];
a.xs[i]-=b.xs[i];
}
if(a.xs[1]<b.xs[1]) a.xs[1]+=10,--a.zs;
a.xs[1]-=b.xs[1],a.zs-=b.zs;
return a;
}
QAQ mul(QAQ a,LL b) {
LL x=0;
for(RI i=K;i>=1;--i)
a.xs[i]=a.xs[i]*b+x,x=a.xs[i]/10,a.xs[i]%=10;
a.zs=a.zs*b+x;return a;
}
void prework() {
f[0][0][0][0]=1;
for(RI i=0;i<50;++i)
for(RI j=0;j<=i*(i+1)/2;++j)
for(RI k=0;k<=i;++k)
for(RI t=0;t<=1;++t) {
f[i+1][j][0][t^1]+=f[i][j][k][t];
f[i+1][j+k+1][k+1][t]+=f[i][j][k][t];
}
for(RI i=1;i<=50;++i)
for(RI j=0;j<i*(i+1)/2;++j) {
QAQ tmp=getQAQ(i*(i+1)/2,i*(i+1)/2-j);
for(RI t=0;t<=1;++t) {
LL js=0;
for(RI k=0;k<=i;++k) js+=f[i][j][k][t];
if(t) ans[i]=ans[i]+mul(tmp,js);
else ans[i]=ans[i]-mul(tmp,js);
}
}
}
int main()
{
scanf("%d",&T);
prework();
while(T--) scanf("%d",&n),print(ans[n]);
return 0;
}