HNOI2011 XOR和路径

题目链接:戳我

对于异或,有一个套路是要把每一位拆开算贡献,这个题就是这样子的。

对于当前位,我们设\(dp[i]\)表示从i到n的路径上该位为1的概率。
(为什么不设\(dp[i]\)表示从1到i的路径上的概率呢,因为有可能当前点到达不了n)

\[dp[u]=\sum_{w(u,v)=1}\frac{1-dp[v]}{du[u]}+\sum_{w(u,v)=0}\frac{dp[v]}{du[u]} \]

然后因为这个不能线性递推,所以我们设未知数,用高斯消元解方程就行了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 101
using namespace std;
int n,m,t;
int du[MAXN+10],head[MAXN+10];
double ans;
double f[MAXN+10][MAXN+10],sum[MAXN+10];
struct Edge{int nxt,to,dis;}edge[200010];
inline void add(int from,int to,int dis){edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t;}
inline void init(int pos)
{
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++) f[i][i]=1.0;
    for(int i=1;i<n;i++)
        for(int j=head[i];j;j=edge[j].nxt)
        {
            int v=edge[j].to;
            if(edge[j].dis&(1<<pos)) f[i][v]+=1.0/du[i],f[i][n+1]+=1.0/du[i];
            else f[i][v]-=1.0/du[i];
        }
}
inline void guass()
{
    for(int i=1;i<=n;i++)
    {
        int pos=i;
        for(int j=i+1;j<=n;j++)
            if(fabs(f[j][i])>fabs(f[pos][i]))
                pos=j;
        if(pos!=i) swap(f[pos],f[i]);
        double cur=f[i][i];
        for(int j=i+1;j<=n+1;j++) f[i][j]/=cur;
        for(int j=i+1;j<=n;j++)
        {
            cur=f[j][i];
            for(int k=i+1;k<=n+1;k++)
                f[j][k]-=f[i][k]*cur;
        }
    }       
    sum[n]=f[n][n+1];
    for(int i=n-1;i>=1;i--)
    {
        sum[i]=f[i][n+1];
        for(int j=i+1;j<=n;j++) sum[i]-=f[i][j]*sum[j];
    }
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w),du[u]++;
        if(u!=v) add(v,u,w),du[v]++;
    }
    for(int i=0;i<=31;i++)
    {
        init(i);
        guass();
        ans+=1.0*(1ll<<i)*sum[1];
    }
    printf("%.3lf\n",ans);
    return 0;
}
posted @ 2019-05-16 12:55  风浔凌  阅读(213)  评论(0编辑  收藏  举报