洛谷3343(ZJOI2015)地震后的幻想乡

题目:https://www.luogu.org/problemnew/show/P3343

1.那个时间与边的大小排名有关,所以需要求一下最大边的期望排名就行。

2.期望排名是这样算的:(排名为1的概率 * 1(1的值) + 排名为2的概率 * 2 + ……) / (m+1)

  仔细一想可以变成这样:(排名>=1的概率 + 排名>=2的概率 + ……) / (m+1)

  可以是这样?——(连0时不能联通的概率 + 连1时不能联通的概率 + ……) / (m+1)

    这里的0、1等可以看作是“前几条边”,也就是“几条边”;

3.这些概率不用每一步都就是概率,可以算的是方案、最后再除以所有方案数;

  这个“几条边”的方案可以用dp来推(只要注意转移到它的状态里的边不重复)!状压记录点集。

  发现  用了 i 条边不连通的方案数 + 用了 i 条边连通的方案数 = 从m条边里选 i 条边的方案数,所以求出一个就能算出另一个了。

    如果求“用了 i 条边连通的方案数”,每一种情况由三部分构成:一条边和这条边两边的两个点集;

      我们需要枚举一条被选的边,再枚举一个端点的点集,剩下的点构成另一个点集,这两个点集的“连通”方案数相乘(还要枚举其中一个点集用了多少条边)。

      但状态里记录的是点集,要枚举那条边有点麻烦。

    如果求“用了 i 条边不连通的方案数”,每一种情况就只由两个点集转移来,无需有中间那条边;

      枚举这两个点集时为了不重不漏,可以确定一个“划分点”,枚举的那个点集必须包含它,并限制枚举的这个点集是连通的;然后剩下的点随便连边(组合数)。

(2018.6.16 PS:这个点必须被限制在一个连通块里。不能把它限制在随便的那个部分里,会重复。一条边也没有的图就是一个例子。)

4.代码里:组合数的赋初值!别忘了zh[0][0]!!还应注意一些地方从0或从1开始。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,dp[2][55][(1<<10)+5],lm;
int siz[(1<<10)+5],zh[55][55];
int head[15],num[1<<10],pre[15];
double ans;
bool vis[15],in[15],ed[110];
struct Edge{
    int next,to;
    Edge(int n=0,int t=0):next(n),to(t) {}
}edge[110];
int dfs(int cur)
{
    vis[cur]=1;int ret=0;
    for(int i=head[cur],v;i;i=edge[i].next)
    if(in[v=edge[i].to])
    {
        if(!ed[i])ret++,ed[i]=1;
        if(!vis[v])ret+=dfs(v);
    }
    return ret;
}
void init()
{
    lm=(1<<n);
    for(int i=1;i<=n;i++)num[1<<(i-1)]=i;
    for(int i=1;i<lm;i++)
    {
        int k=i;
        memset(ed,0,sizeof ed);memset(vis,0,sizeof vis);
        memset(in,0,sizeof in);
        while(k)in[num[k&(-k)]]=1,k-=(k&(-k));
        for(int j=1;j<=n;j++)if(in[j])siz[i]+=dfs(j);siz[i]>>=1;
    }
    zh[0][0]=1;//!!!!!!!
    for(int i=1;i<=m;i++)
    {
        zh[0][i]=1;
        for(int j=1;j<i;j++)
            zh[j][i]=zh[j][i-1]+zh[j-1][i-1];
        zh[i][i]=1;
    }
    for(int i=0;i<lm;i++)dp[0][0][i]=1;
    for(int i=1;i<=n;i++)dp[1][0][1<<(i-1)]=1,dp[0][0][1<<(i-1)]=0;
}
int main()
{
//    freopen("zjsc.out","w",stdout);
    scanf("%d%d",&n,&m);int u,v;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        edge[i]=Edge(head[u],v);head[u]=i;
        edge[i+m]=Edge(head[v],u);head[v]=i+m;
//        dp[1][1][(1<<(u-1))|(1<<(v-1))]=1;
    }
    init();
//    for(int i=1;i<lm;i++)if(!dp[1][1][i])dp[0][1][i]=1;
    for(int i=1;i<=m;i++)
        for(int s=1;s<lm;s++)
        {
            int k=1;for(;(s&(1<<(k-1)))==0;k++);
            for(int c=((s-1)&s);c;c=((c-1)&s))
                if(c&(1<<(k-1)))
                {
//                    if(i==1)printf("s=%d c=%d\n",s,c);
                    for(int j=0;j<=i;j++)//0~i
                    {
//                        if(i==1&&j==1)printf(" dp1=%d\n",dp[1][j][c]);
                        dp[0][i][s]+=dp[1][j][c]*zh[i-j][siz[s^c]];
//                        if(i==1&&s==7&&c==5)
//                            printf("j=%d dp1jc=%d zh=%d dp0is=%d\n",
//                            j,dp[1][j][c],zh[i-j][siz[s^c]],dp[0][i][s]);
//                        if(i==1&&dp[0][i][s])printf("j=%d s=%d dp0=%d\n",j,s,dp[0][i][s]);
                    }
                }
            dp[1][i][s]=zh[i][siz[s]]-dp[0][i][s];
//            if(i==1)printf("(s=%d dp1=%d)\n",s,dp[1][i][s]);
        }
//    for(int i=1;i<=n;i++)
//    {
//        pre[i]=dp[0][i-1][lm-1]-dp[0][i][lm-1];
//        pre[i]/=zh[i][n];
//    }
//    for(int i=1;i<=n;i++)
//        ans+=(double)i/(n+1)*pre[i];
    for(int i=0;i<m;i++)ans+=(double)dp[0][i][lm-1]/(double)zh[i][m];//其实是<m,但dp[0][m][lm-1]肯定是0 
    printf("%.6lf",(double)ans/(double)(m+1));//已求者为排名期望 
    return 0;
}

 

  

posted on 2018-05-31 23:00  Narh  阅读(180)  评论(0编辑  收藏  举报

导航