这道题在比赛的时候已经搞出来了大半,但是因为精度控制的不熟悉以及意识的模糊
导致最后也没有出来,赛后LL改了改细节和精度控制就过了,还是代码量太少了
首先可以注意到数据,给出的是最多不超过10条,最长不超过1000的链表
设cnt[i]为链i的长度,因此总的方案数fm=(cnt[1]+1)*(cnt[2]+1)*……*(cnt[n]+1)-1(题目要求至少取一个碎片)
则答案可以为总的几率/fm
因可以分别看两个部分,一个是所有碎片的总和,另一个是套装加成
先看第二个部分套装加成,对于每个套装加成,可先将该level存在的数给提取出来,我是放在t_val,right里面
right存放对应数的链表的右边还有多长
两个系数xishu,t_xishu分别表示
该链的个数不足i个的链的长度乘积,和该链的个数大于等于i个的链的长度乘积
因此可以枚举取法,然后t_xishu*xishu就是这个套装出现的次数,然后按照题意公式给出就行了
现在再来看第一个部分,因为我们已经枚举了所有的套装情况(除了1),因此只需要把上面说到的情况数*s,再加上只取一个链上的某一碎片的情况,就是第一部分的总和
最后一定要把两个部分先相加,再除以fm,因为多次除法会使得精度丢失非常厉害(fm估算可以到10^30)
代码如下:
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define MM(a,b,c) memset(a,b,sizeof(a[0])*(c+2))
using namespace std;
const int SIZEN=10005;
struct edge{
int to,next;
};
edge e[SIZEN];
int val[SIZEN];
int head[SIZEN],sz,in[SIZEN];
int right[15],t_val[15];
int ll,cnt,bit[1050];
vector<int> num[15];
double fm;
void getbit(){
for(int i=1;i<1024;i++){
int ti=i;
for(int j=0;j<10;j++){
bit[i]+=ti&1;
ti>>=1;
}
}
}
void init(int n){
MM(head,-1,n);
MM(in,0,n);
sz=ll=cnt=0;
}
void addedge(int u,int v){
e[sz].to=v;
e[sz].next=head[u];
head[u]=sz++;
in[v]++;
}
void dfs(int u,int id){
num[id].push_back(u);
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
dfs(v,id);
}
}
void rebuild(int n){
for(int i=0;i<n;i++)
if(!in[i]){
num[cnt].clear();
dfs(i,cnt);
ll=max(ll,(int)num[cnt].size());
cnt++;
}
}
void debug1(){
for(int i=0;i<cnt;i++){
for(int j=0;j<num[i].size();j++) printf("%d ",num[i][j]);
printf("\n");
}
}
double get_ans(){
fm=1;
int ccnt;
double xishu,t_xishu;
double s,ans=0;
for(int i=0;i<cnt;i++)
fm*=(int)(num[i].size()+1);
for(int i=0;i<ll;i++){
ccnt=0;t_xishu=1;
for(int j=0;j<cnt;j++){
if(num[j].size()>=i+1){
right[ccnt]=(num[j].size()-i);
t_val[ccnt++]=val[num[j][i]];
}
else t_xishu*=(double)(num[j].size()+1);
}
for(int j=1;j<(1<<ccnt);j++){
xishu=1;s=0;
int tj=j;
for(int k=0;k<ccnt;k++){
if(tj&1){
xishu*=right[k];
s+=t_val[k];
}
else xishu*=i+1;
tj>>=1;
}
ans+=s*xishu*t_xishu;
if(bit[j]>=2) ans+=s*xishu*t_xishu*bit[j]/ccnt;
}
}
return ans/(fm-1);
}
void solve(){
int n,m;
int u,v;
scanf("%d%d",&n,&m);
init(n);
for(int i=0;i<n;i++)
scanf("%d",&val[i]);
for(int i=0;i<m;i++){
scanf("%d%d",&u,&v);
addedge(u,v);
}
rebuild(n);
double ans=get_ans();
printf("%.3lf\n",ans);
}
int main()
{
int _;
scanf("%d",&_);
getbit();
while(_--) solve();
}