[HNOI2011]XOR和路径

题面在这里

题意:给定一个无向图,从1号节点出发,每次等概率选择连接该节点的一条边走到另一个节点,到达n号节点时,将走过的路径上的所有边权异或起来,求这个异或和的期望

sol

一道期望大火题(表示看了zsy大佬ycb大佬的题解才过去的orz)

递推期望,因为是异或和,按照正常方法会很难,于是考虑按位DP(套路吧),即对于边权在二进制下的每一位分别讨论
设状态的时候需要注意
如果设\(f[x]\)表示从1号节点到达x号节点且异或和为1的概率
那么在转移的时候,因为到达n号节点的时候就已经停止,所以f[n]不能转移;而我们又必须求出\(f[n]\),因此必须先对除n以外的所有点进行计算,再推到n,这样会很麻烦
于是想到倒着推,设\(f[x]\)表示从x号节点到达n号节点且异或和为1的概率,答案为\(f[1]\),虽然说也不能从\(f[n]\)转移,但因为要求解的不是\(f[n]\)所以就让求解变得可行了

通过边进行转移:

\[f[u]=\sum_{v}\frac{f[v]}{d[u]}*[(u,v)==0]+\sum_{v}\frac{1-f[v]}{d[u]}*[(u,v)==1] \]

意即该位权值为1的点通过0边和该位权值为0的点通过1边到达点u所得的该位权值都是1

由于每个f[u]都和另外的f[v]产生依赖关系,故无法直接递推求解
高斯消元大显身手啦!!!!!!

代码

#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<iomanip>
#include<cstring>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define mp make_pair
#define pb push_back
#define RG register
#define il inline
using namespace std;
const int mod=1e9+7;
const int N=110;
const double eps=1e-10;
typedef unsigned long long ull;
typedef vector<int>VI;
typedef long long ll;
typedef double dd;
il ll read(){
	RG ll data=0,w=1;RG char ch=getchar();
	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
	return data*w;
}

int n,m,head[N],nxt[N*N*2],to[N*N*2],val[N*N*2],d[N],cnt;
il void add(int u,int v,int w){
	to[++cnt]=v;d[v]++;
	val[cnt]=w;
	nxt[cnt]=head[u];
	head[u]=cnt;
}

dd S[35][N][N],ans[35][N],sum;
il bool gauss(int a){
	//高斯消元部分
	for(RG int i=1;i<=n;i++){
		for(RG int j=i;j<=n;j++)
			if(abs(S[a][j][i])>eps){
				swap(S[a][j],S[a][i]);break;
			}
		if(abs(S[a][i][i])<=eps)return 0;
		for(RG int j=i+1;j<=n;j++){
			ans[a][j]-=ans[a][i]*S[a][j][i]/S[a][i][i];
			for(RG int k=n;k>=i;k--)
				S[a][j][k]-=S[a][i][k]*S[a][j][i]/S[a][i][i];
		}
	}

	for(RG int i=n;i;i--){
		for(RG int j=i+1;j<=n;j++)
			ans[a][i]-=S[a][i][j]*ans[a][j];
		ans[a][i]/=S[a][i][i];
	}
	
	return 1;
}

int main()
{
	n=read();m=read();
	for(RG int i=1,u,v,w,t;i<=m;i++){
		u=read();v=read();w=read();t=0;
		add(u,v,w);if(u!=v)add(v,u,w);
	}

    //这里是统计系数
	for(RG int u=1;u<n;u++){
		for(RG int i=0;i<=32;i++)S[i][u][u]+=1.0;
		for(RG int i=head[u];i;i=nxt[i]){
			RG int v=to[i],t=0;
			while(t<=32){
				S[t][u][v]+=((val[i]&1)?1:(-1))*1.0/(d[u]*1.0);
				ans[t][u]+=((val[i]&1)?1:0)*1.0/(d[u]*1.0);
				val[i]>>=1;t++;
			}
		}
	}for(RG int i=0;i<=32;i++)S[i][n][n]+=1.0;
	//这样可以保证最后f[n]==0消除f[n]的影响
	
	for(RG int i=0;i<=32;i++)gauss(i);
	
	for(RG ll i=0,x=1;i<=32;i++){sum+=ans[i][1]*x;x<<=1;}
	//按位统计答案
	
	printf("%.3lf\n",sum);
	return 0;
}

注:还有一道[HNOI2013]游走和此题思想类似,题解在这里

posted @ 2018-02-01 22:14  cjfdf  阅读(148)  评论(0编辑  收藏  举报