版权声明:这篇文章的作者是个蒟蒻,没有转载价值,如果要转说一下好了 https://blog.csdn.net/litble/article/details/83004089
题目分析
朋友,你听说过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;
}