其他分享
首页 > 其他分享> > P3211 [HNOI2011]XOR和路径 (拆位+高斯消元)

P3211 [HNOI2011]XOR和路径 (拆位+高斯消元)

作者:互联网

https://www.luogu.com.cn/problem/P3211

题目描述

给定一个无向连通图,其节点编号为 1 到 N,其边的权值为非负整数。试求出一条从 1 号节点到 N 号节点的路径,使得该路径上经过的边的权值的“XOR 和”最大。该路径可以重复经过某些节点或边,当一条边在路径中出现多次时,其权值在计算“XOR 和”时也要被重复计算相应多的次数。

直接求解上述问题比较困难,于是你决定使用非完美算法。具体来说,从 1 号节点开始,以相等的概率,随机选择与当前节点相关联的某条边,并沿这条边走到下一个节点,重复这个过程,直到走到 N 号节点为止,便得到一条从 1 号节点到 N 号节点的路径。显然得到每条这样的路径的概率是不同的并且每条这样的路径的“XOR 和”也不一样。现在请你求出该算法得到的路径的“XOR 和”的期望值。

输入格式

输入文件的第一行是用空格隔开的两个正整数N和M,分别表示该图的节点数和边数。紧接着的M行,每行是用空格隔开的三个非负整数u,v和w(1≤u,v≤N,0≤w≤10^9),表示该图的一条边(u,v),其权值为w。输入的数据保证图连通。

输出格式

输出文件仅包含一个实数,表示上述算法得到的路径的“XOR 和”的期望值,要求保留三位小数。(建议使用精度较高的数据类型进行计算)

输入输出样例

输入 #1
2 2
1 1 2
1 2 3
输出 #1
2.333

说明/提示

样例解释:有1/2的概率直接从1号节点走到2号节点,该路径的“XOR和”为3;有1/4的概率从1号节点走一次1号节点的自环后走到2号节点,该路径的“XOR和”为1;有1/8的概率从1号节点走两次1号节点的自环后走到2号节点,该路径的“XOR和”为3…依此类推,可知“XOR和”的期望值为:3/2+1/4+3/8+1/16+3/32+…=7/3,约等于2.333。

30%的数据满足N≤30
100%的数据满足2≤N≤100,M≤10000,但是图中可能有重边或自环。

 

解:求异或和,考虑拆位,每一位只有0和1,所以拆位之后异或的结果也是只有0和1。结果只有0或1就好搞了。设f[u]表示考虑当前二进制位上u->n路径的异或和为1的期望,f[1]就是1->n路径异或和的期望,最后结果就是Σ 2i * f[1]。

对所有(u,v)€E,f[u]=1/deg[u] * (∑w(u,v)=0f[v]+∑w(u,v)=1(1-f[v]) )这个式子其实很明显,因为从u到v,边权为0不会改变异或和,而边权为1会改变。

移项后得到 deg[u] * f[u] - ∑w(u,v)=0f[v] + ∑w(u,v)=1f[v] = ∑w(u,v)=11。就可以列出n个方程组。

因为f[n] = 0;所以不需要管第n行。高斯消元然后对每一位贡献求和就是答案。

#include<bits/stdc++.h>
#define sd(x) scanf("%d",&x)
#define lsd(x) scanf("%lld",&x)
#define sf(x) scanf("%lf",&x)
#define ms(x,y) memset(x,y,sizeof x)
#define fu(i,a,b) for(register int i=a;i<=b;i++)
#define fd(i,a,b) for(register int i=a;i>=b;i--)
#define all(a) a.begin(),a.end()
#define range(a,x,y) a+x,a+y+x
#define dbug printf("bbbk\n")
#define R register
using namespace std;
using namespace __gnu_cxx;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int,int> P;
const int N=1e5+99;
const int MN=1e7+9;
const ll mod=1e4+7;
const int MAX=1e9;
const ll INF=1e9+7;
int n,m;
int d[109];
vector<P> w[109];
double a[109][109];
double eps=1e-10;
double guass()
{
    int now=1;
    fu(i,1,n)
    {
        int p=now;
        fu(j,now+1,n)
        {
            if(fabs(a[j][i])>fabs(a[p][i])) p=j;
        }
        if(fabs(a[p][i])<=eps) continue;
        swap(a[now],a[p]);
        fu(j,1,n)
        {
            if(j==now) continue;
            if(fabs(a[j][i])<eps) continue;
            double rate=a[j][i]/a[now][i];
            fu(k,i,n+1) a[j][k]-=a[now][k]*rate;
        }
        now++;
    }
    return a[1][n+1]/a[1][1];
}
int main()
{
    cin>>n>>m;
    int mxk=0;
    fu(i,1,m)
    {
        int u,v,weight;cin>>u>>v>>weight;
        if(u==v) 
        {
            d[u]++;//自环
            w[u].push_back(P(u,weight));
        }
        else 
        {
            d[u]++,d[v]++;
            w[u].push_back(P(v,weight));
            w[v].push_back(P(u,weight));
        }
    }
    double ans=0;
    fu(st,0,30)//二进制位数
    {
        ms(a,0);
        fu(i,1,n-1)//f[n]=0,所以跳过n行
        {
            a[i][i]=d[i];
            for(P p:w[i])
            {
                int j=p.first,wt=p.second;
                if((wt>>st)&1) a[i][j]++,a[i][n+1]++;
                else a[i][j]--;
            }
        }
        ans+=guass()*(1<<st);
    }
    printf("%.3f\n",ans);
    return 0;
}

 

标签:XOR,fu,int,路径,HNOI2011,节点,高斯消,define
来源: https://www.cnblogs.com/studyshare777/p/14715617.html