[HNOI2011]XOR和路径 题解

\(f(x)\) 表示从 \(x\) 节点走到 \(n\) 的期望。有 \[f(x)=\sum_{\{x,y\}}\frac{f(y)\oplus w(x,y)}{{\rm deg}(x)}\] 由于有后效性,无法 DP 求得。于是可以将其看作未知数,\(n\) 个点构成 \(n\)\(n\) 元一次方程,解方程即可。
但还是不太好求,考虑期望的线性性,按位处理。
重新记 \(f(x)\) 表示当前位的 \(x\) 走到 \(n\) 异或和为 \(1\) 的概率,有 \[{\rm deg}(x)f(x)=\sum_{w(x,y)=1}f(y)+\sum_{w(x,y)=0}\big(1-f(y)\big)\] 最后的答案为 \(\sum\limits_{t=1}^{32} 2^{t-1}\cdot f(1)\)
为了消元简便,实际求的过程中我们把方程写成 \[\sum_{w(x,y)=0}f(y)-\sum_{w(x,y)=1}f(y)-{\rm deg}(x)f(x)=-\sum_{w(x,y)=1}1\]
注意自环只用算一次。
别颓废了……抓紧时间……

#include <bits/stdc++.h>
using namespace std;

const int N=105,M=10005;
const double eps=1e-8;
struct Edge{int to,nxt,w;}e[M<<1];
int n,m,cnt,head[N],deg[N];
double ans,a[N][N];

inline void add(int u,int v,int w) {e[++cnt]=(Edge){v,head[u],w};head[u]=cnt;}

void gauss()
{
    for(int i=1;i<=n;++i)
    {
        int t=i;
        for(int j=i+1;j<=n;++j)
            if(fabs(a[j][i])>fabs(a[t][i])) t=j;
        for(int j=i;j<=n+1;++j) swap(a[t][j],a[i][j]);
        for(int j=n+1;j>=i;--j) a[i][j]/=a[i][i];
        for(int j=1;j<=n;++j)
            if(i!=j)
                for(int k=n+1;k>=i;--k)
                    a[j][k]-=a[j][i]*a[i][k];
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,d,b,c;i<=m;++i)
    {
        scanf("%d%d%d",&d,&b,&c);
        if(d==b)
        {
            deg[d]++; add(d,b,c);
        }
        else
        {
            deg[d]++,deg[b]++;
            add(d,b,c); add(b,d,c);
        }
    }
    for(int k=30;~k;--k)
    {
        memset(a,0,sizeof(a));
        for(int u=1;u<n;++u)
        {
            a[u][u]=-deg[u];
            for(int i=head[u];i;i=e[i].nxt)
            {
                int v=e[i].to,w=(e[i].w>>k)&1;
                if(w) a[u][v]-=1,a[u][n+1]-=1;
                else a[u][v]+=1;
            }
        }
        a[n][n]=1; gauss();
        ans+=(1<<k)*a[1][n+1];
    }
    printf("%.3lf",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wzzyr24/p/12315439.html