zoukankan      html  css  js  c++  java
  • bzoj 2337: [HNOI2011]XOR和路径

    Description

    给定一个无向连通图,其节点编号为 1 到 N,其边的权值为非负整数。试求出一条从 1 号节点到 N 号节点的路径,使得该路径上经过的边的权值的“XOR 和”最大。该路径可以重复经过某些节点或边,当一条边在路径中出现多次时,其权值在计算“XOR 和”时也要被重复计算相应多的次数。
    直接求解上述问题比较困难,于是你决定使用非完美算法。具体来说,从 1 号节点开始,以相等的概率,随机选择与当前节点相关联的某条边,并沿这条边走到下一个节点,重复这个过程,直到走到 N 号节点为止,便得到一条从 1 号节点到 N 号节点的路径。显然得到每条这样的路径的概率是不同的并且每条这样的路径的“XOR 和”也不一样。现在请你求出该算法得到的路径的“XOR 和”的期望值。

    solution

    正解:高斯消元
    根据期望的线性性,要求的东西可以拆成每一位二进制位的期望之和
    考虑从i到达n时该二进制位为1的方案,分边权是否为1讨论:
    (f[j]+=f[i]/du[i]) 如果边权该位为0
    (f[j]+=(1-f[i])/du[i]) 如果边权的该位为1
    考虑到这是有向图,且存在环,所以不能拓扑序DP,用高斯消元可以解:
    我们把 f[1],f[2]...f[n]看做是变量,然后du[i]看做是系数,就可以求了
    但是做出来是WA的?一看题解仿佛必须要逆向推啊?原因仿佛是正推会存在不能走到i的概率?

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #define RG register
    #define il inline
    #define iter iterator
    #define Max(a,b) ((a)>(b)?(a):(b))
    #define Min(a,b) ((a)<(b)?(a):(b))
    using namespace std;
    typedef long long ll;
    const int N=105,M=20005;
    int n,m,num=0,du[N],head[M],to[M],dis[M],nxt[M];
    void link(int x,int y,int z){
    	nxt[++num]=head[x];to[num]=y;head[x]=num;dis[num]=z;}
    double a[N][N],ans=0;
    inline double solve(){
    	for(int l=1;l<=n;l++){
    		int maxid=l;
    		for(int i=l+1;i<=n;i++)
    			if(fabs(a[i][l])>fabs(a[maxid][l]))maxid=l;
    		if(l!=maxid)swap(a[l],a[maxid]);
    		for(int i=l+1;i<=n;i++){
    			if(!a[i][l])continue;
    			double tmp=a[l][l]/a[i][l];
    			for(int j=l;j<=n+1;j++)
    				a[i][j]=a[l][j]-tmp*a[i][j];
    		}
    	}
    	for(int i=n;i>=1;i--){
    		for(int j=i+1;j<=n;j++)
    			a[i][n+1]-=a[i][j]*a[j][n+1];
    		a[i][n+1]/=a[i][i];
    	}
    	return a[1][n+1];
    }
    void work()
    {
    	int x,y,z;
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++){
    		scanf("%d%d%d",&x,&y,&z);
    		link(x,y,z);du[y]++;
    		if(x!=y)link(y,x,z),du[x]++;
    	}
    	for(int w=0;w<=30;w++){
    		memset(a,0,sizeof(a));
    		for(int i=1;i<n;i++){
    			a[i][i]=1.0;
    			for(int j=head[i];j;j=nxt[j]){
    				int u=to[j];
    				if(dis[j]&(1<<w))
    					a[i][u]+=1.0/du[i],a[i][n+1]+=1.0/du[i];
    				else a[i][u]-=1.0/du[i];
    			}
    		}
    		a[n][n]=1.0;
    		ans+=solve()*(1<<w);
    	}
    	printf("%.3lf
    ",ans);
    }
    
    int main(){
    	work();
    	return 0;
    }
    
    
  • 相关阅读:
    Properties类读取配置文件
    HashMap,Hashtable,TreeMap ,Map
    观察者模式(Observer和Observable实现)
    HashSet和TreeSet
    ArrayList,Vector,LinkedList
    定时调度(定时器)的使用
    序列化与反序列化
    对象比较器:Comparable和Comparator
    final finally finalize 区别
    (转载)oracle 在一个存储过程中调用另一个返回游标的存储过程
  • 原文地址:https://www.cnblogs.com/Yuzao/p/8017346.html
Copyright © 2011-2022 走看看