【NOIP模拟】闲荡

题面

L 饭后无聊,便在 BugTown 里闲荡。  BugTown 共有 N 栋房屋和 M 条有向道路。每栋房屋都有一个非负整数 vi 作为标识。  BugTown 有一个特性十分神奇:从任意一个房屋离开后沿着路走再也不会回到原地。  L 想选一个房屋作为闲荡的起点,之后,他会随机选择一条当前位置能走的道路顺其 走过去,如此反复直到没有能走的道路。  由于极度无聊, L 发明了一个游戏以为消遣。他在闲荡的过程中记录已经过的房屋标 识的异或和(含起点)。闲荡完后,他会得到一个数。  L希望对每个房屋算出以它为起点能得到的数的期望值,但是他不知道怎么算,只好 求助于你。

对于 10% 的数据, N <= 5; M <= 10。  对于 30% 的数据, N, M<= 50。  对于 70% 的数据, N, M <=1000。  对于 100% 的数据, 1 <= N,M <= 1e5; 0 <= vi <= 1e9。

分析

期望dp.

很久没碰了,今天算是彻头彻尾好好复习了一下

通俗地讲,我们定义一个随机变量,数学期望是随机变量取值与概率的乘积之和。

而此题的随机变量取值就是异或和,概率就是u的出度分之一。

此题70%数据才1000,可以过n,而我一个julao同学跑到了90分的暴力(直接枚举每个点,直到dfs到叶子结点)

要考虑如何递推,首先要知道关于期望的两个结论:

1.E(A+B)=E(A)+E(B),这意味着我们可以把要求的期望拆分成和的形式

2.E(K*A)=K*E(A),其中K为常数。这意味着我们一定条件下能简化求期望值的复杂度。

因为异或是在二进制位上进行的操作,我们可以把要求的期望值写成 X=∑ai  *  2i

E(X)=∑E(ai  *  2i)=∑E(a)*  2i

所以我们只需要求E(a)即可

设P为概率,v为当前点的标识值,此时标识值为10进制处理,它是随机变量

但当依次对每个二进制位操作时,二进制的0和1就成为了新的随机变量,根据两条性质,它们最后通过乘2的次方可以被还原回去

则有 E(a)=∑v *P(ai=v) = 0* P(ai=0) + 1*P(ai=1)   = P(ai=1)

所以求的期望值实际上是每个二进制位为1的概率

用f[c][d]表示从点c出发计算的值的二进制当前位为d(d=0或1)的概率。

对于没有出边的点,我们可以直接得出值(如果这个点的这一位为g,那么f[c][g] = 1f[c][g ^ 1] = 0)。

对于其他的f,我们可以递推得到。具体实现可以用记忆化搜索或先求拓扑序再dp

#include<bits/stdc++.h>
using namespace std;
#define N 100010
int n,m,tot,cnt;
double ans[N];
int val[N],first[N],in[N],s[N];
double f[N][2];
queue<int>q;
struct email
{
    int u,v;
    int nxt;
}e[N*4];
inline void add(int u,int v)
{
    e[++cnt].nxt=first[u];first[u]=cnt;
    e[cnt].u=u;e[cnt].v=v;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&val[i]);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);in[v]++;
    }
    for(int i=1;i<=n;i++)
        if(!in[i])
            q.push(i);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        s[++tot]=u;
        for(int i=first[u];i;i=e[i].nxt)
        {
            int v=e[i].v;
            in[v]--;
            if(!in[v])
                q.push(v);
        }        
    }
    for(int i=0;i<30;i++)
        for(int j=n;j>=1;j--)
        {
            int u=s[j],siz=0;
            int g=((val[u]>>i)&1);
            for(int k=first[u];k;k=e[k].nxt)
                siz++;
            if(!siz)
            {
                f[u][g]=1.0;
                f[u][g^1]=0.0;
            }
            else
            {
                double p=1.0/siz;
                f[u][1]=0.0;f[u][0]=0.0;
                for(int k=first[u];k;k=e[k].nxt)
                {
                    int v=e[k].v;
                    f[u][0]+=f[v][g]*p;//相同为0,不同为1 
                    f[u][1]+=f[v][g^1]*p;
                }
            }
            ans[u]+=(1<<i)*f[u][1];
        }
    
    for(int i=1;i<=n;i++)
        printf("%.3lf
",ans[i]);
    return 0;
}
“Make my parents proud,and impress the girl I like.”
原文地址:https://www.cnblogs.com/NSD-email0820/p/9483825.html