题面
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,可以过n2 ,而我一个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(ai )* 2i
所以我们只需要求E(ai )即可
设P为概率,v为当前点的标识值,此时标识值为10进制处理,它是随机变量
但当依次对每个二进制位操作时,二进制的0和1就成为了新的随机变量,根据两条性质,它们最后通过乘2的次方可以被还原回去
则有 E(ai )=∑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] = 1,f[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; }