【HDU 4921】Map

这道题在比赛的时候已经搞出来了大半,但是因为精度控制的不熟悉以及意识的模糊

导致最后也没有出来,赛后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();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值