1. 程式人生 > >HDU4624 Endless Spin Min-Max容斥+DP

HDU4624 Endless Spin Min-Max容斥+DP

題目分析

朋友,你聽說過Min-Max容斥嗎?

所謂Min-Max容斥就是這樣一個式子: E ( m a x ( S )

) = T S ( 1
) S + 1 E (
m i n ( T ) ) E(max(S))=\sum_{T \subset S} (-1)^{|S|+1}E(min(T)) 。所謂的max,就是集合內最後一個被選中的元素被選中的期望代價,min就是第一個被選中的元素被選中的期望代價。

那麼這題用一個Min-Max容斥,問題就轉化為求每一個集合裡的點,至少選中一個的期望。那麼只要算出每一次決策一個都選不中的概率,也就是選擇一次,選擇的區間不包含這個集合裡任何點的概率就行了。

複雜度 O ( 2 n ) O(2^n) ,你只需要把評測機換成神威太湖之光,就能AC此題。

設一個DP狀態, f ( i , j , k , 0 / 1 ) f(i,j,k,0/1) ,表示考慮前 i i 個球是否在集合中,有 j j 個區間可以保證不選到在集合中的球,最後一個選擇的球到 i i 位置之間有 k k 個空位,集合中選擇的球的數量是偶數/奇數的狀態數,轉移通過考慮第 i + 1 i+1 個球選不選有兩種:

f ( i + 1 , j , 0 , 1 / 0 ) + = f ( i , j , k , 0 / 1 ) f(i+1,j,0,1/0)+=f(i,j,k,0/1)
f ( i + 1 , j + k + 1 , k + 1 , 0 / 1 ) + = f ( i , j , k , 0 / 1 ) f(i+1,j+k+1,k+1,0/1)+=f(i,j,k,0/1)

這個DP是 O ( n 4 ) O(n^4) 的。

假設共有 i i 個球,某個集合 T T j j 個區間可以保證不選到在集合內的球,那麼 E ( m i n ( T ) ) = ( i + 1 ) i 2 ( i + 1 ) i 2 j E(min(T))=\frac{\frac{(i+1)i}{2}}{\frac{(i+1)i}{2}-j} ,通過考慮集合內球數量的奇偶性,就可以做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;
}