内容简介:这道题我用了一个特别神奇的做法,然后被卡精度卡成90分了,然后我不要face地用了__float128过了此题。一看就是一道积分题。对于任意实数$x\in [0,1]$,我们可以求出最小生成树的最大边权不超过x的概率,如果我们把x看作未知数,那么对于这张图我们可以得到一个关于x的式子$F(x)$
这道题我用了一个特别神奇的做法,然后被卡精度卡成90分了,然后我不要face地用了__float128过了此题。
一看就是一道积分题。
对于任意实数$x\in [0,1]$,我们可以求出最小生成树的最大边权不超过x的概率,如果我们把x看作未知数,那么对于这张图我们可以得到一个关于x的式子$F(x)$
那么现在原问题可以分解为两个小问题:
如何求出$F(x)$
已知$F(x)$怎么计算答案
先来解决第一个。考虑如果x是一个确定的实数而不是未知数怎么做。我们设$f(sta)$为包含sta状态的节点的最小生成树最大边不超过x的概率。那么$f(sta)$相当于是计算用不超过x的边能使图联通的概率。
我们可以用总概率1减去不合法的概率来进行转移。
如何计算不合法的概率?考虑枚举编号最小的节点所在联通块有哪些点,然后子集枚举进行转移。为了节省时间,我们不妨只处理所有包含1号点的sta即可。那么有:
$$f(sta)=1-\displaystyle\sum_{sta1\subseteq sta,1\in sta1} f(sta1)\times (1-x)^{(\displaystyle\sum_{i=1}^n\displaystyle\sum_{j=1}^n [i\in sta1][j \in sta-sta1])}$$
这时我们发现如果把x作为未知数,那么算出来的一定是一个多项式,我们用$f(sta)$维护多项式,然后用多项式运算代替一部分实数运算即可。
由于多项式最高次项是$n^2$级别的,所以这部分时间复杂度是$O(3^n n^4)$的,算了一下$n=10$的时候大约是5亿,应该可以使用FFT优化,但是仔细一想发现没有这个必要,因为这个东西完全跑不满,常数绝对不会超过$\frac{1}{10}$,所以放心暴力算即可。
那么得到了$F(x)$,我们可以直接计算答案了,答案就是:
$$\int_{x=0}^1 F(x)-F(x-dx)$$
设$F(x)=\displaystyle\sum_{i=0}^k a_ix^i$
那么可得答案为:
$$\int_{x=0}^1 \displaystyle\sum_{i=0}^k a_ix^i-a_i(x-dx)^i$$
$$=\int_{x=0}^1 \displaystyle\sum_{i=0}^k a_i\displaystyle\sum_{j=1}^i C_i^j dx^j(-1)^{j-1}$$
$$=\int_{x=0}^1 \displaystyle\sum_{i=0}^k ia_idx$$
$$=\displaystyle\sum_{i=0}^k \frac{i}{i+1}a_i$$
于是就做完了。
但是这个方法直接写会炸精度,得到90分的高分(虽说如果是正式比赛这个分数也够了),然后我face不要地用了__float128过了(事实上用高精度也是可以的,如果你愿意去写实数的高精度那也行,反正我这个菜鸡是不想写)
附上我用了__float128的代码:
#include<bits/stdc++.h> using namespace std; typedef __float128 ld; struct Poly{ vector<ld> a; Poly(ld x0=0,ld x1=0){ a.clear(); if(x0!=0) a.push_back(x0); if(x1!=0) a.push_back(x1); } unsigned size(){ return a.size(); } void resize(int n){ a.resize(n); } inline ld& operator[](int x){ return a[x]; } inline Poly operator+(Poly b){ Poly c; c.a.resize(max((unsigned)a.size(),b.size())); for(int i=0;i<c.size();++i) c[i]=(i<a.size()?a[i]:0)+(i<b.size()?b[i]:0); while(c.size()>0&&c.a.back()==0) c.a.pop_back(); return c; } inline Poly operator-(){ Poly c; c.a.resize(a.size()); for(int i=0;i<a.size();++i) c[i]=-a[i]; return c; } inline Poly operator*(Poly b){ if(a.size()==0||b.size()==0) return Poly(0,0); Poly c; c.a.resize(a.size()+b.size()-1); for(int i=0;i<a.size();++i) for(int j=0;j<b.size();++j) c[i+j]+=a[i]*b[j]; while(c.size()>0&&c.a.back()==0) c.a.pop_back(); return c; } inline long double solve(){ ld ret=0; for(int i=0;i<a.size();++i) ret+=a[i]*i/(i+1); return ret; } }; Poly f[1<<10|5],g[1<<10|5],p[105]; bool e[15][15],mark[1<<10|5]; struct DSF{ int fa[15]; void init(int n){ for(int i=1;i<=n;++i) fa[i]=i; } int find_set(int x){ return fa[x]=(fa[x]==x?x:find_set(fa[x])); } void merge(int x,int y){ fa[find_set(x)]=find_set(y); } }dsf; bool getbit(int sta,int i){ return sta&(1<<i-1); } int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=m;++i){ int a,b; scanf("%d%d",&a,&b); e[a][b]=1; e[b][a]=1; } for(int sta=0;sta<1<<n;++sta){ dsf.init(n); mark[sta]=1; for(int i=1;i<=n;++i) if(getbit(sta,i)) for(int j=1;j<=n;++j) if(getbit(sta,j)) if(e[i][j]) dsf.merge(i,j); int last=0; for(int i=1;i<=n;++i) if(getbit(sta,i)){ if(last==0) last=dsf.find_set(i); if(last!=dsf.find_set(i)) mark[sta]=0; } } p[0]=Poly(1,0); for(int i=1;i<=n*n;++i) p[i]=p[i-1]*Poly(1,-1); f[1]=Poly(1,0); for(int sta=2;sta<1<<n;++sta){ if(!getbit(sta,1)||!mark[sta]) continue; f[sta]=Poly(1,0); for(int sta1=(sta-1)&sta;sta1>0;sta1=(sta1-1)&sta){ if(!getbit(sta1,1)) continue; int cnt=0; for(int i=1;i<=n;++i) if(getbit(sta1,i)) for(int j=1;j<=n;++j) if(e[i][j]&&!getbit(sta1,j)&&getbit(sta,j)) ++cnt; f[sta]=-f[sta1]*p[cnt]+f[sta]; } } printf("%.6Lf",f[(1<<n)-1].solve()); return 0; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 宜宾地震提前预警,地震预测难关终于攻克了吗?
- 花莲地震前 20 秒就已报警,地震精准预测不再遥远?
- 放弃幻想,全面拥抱Transformer
- 去中心化只是幻想?
- “四川长宁6.0级地震,提前61秒预警”安防这10项技术都跟地震有关
- “地震波还有61秒到达”
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。