洛谷 P3211 [HNOI2011]XOR和路径(推dp+高斯消元)

来源:互联网 发布:淘宝店装修收费吗 编辑:程序博客网 时间:2024/06/07 18:22

传送门
首先,异或的话直接讨论不好讨论,那么我们可以按位讨论,对于每一位讨论出来一个结果,然后将结果相加就好了。
然后考虑怎么讨论一位上的结果。
我们可以设出来一个dp方程:f(i)表示i到n的异或和期望值,则初始状态为f(n)=0
dp转移方程就是:其中weight(u,v)代表(u,v)的那一位的值
f(v)=(u,v)Ef(u)/degree(v) weight(u,v)=0
f(v)=(u,v)E(1f(u))/degree(v) weight(u,v)=1
则对于每一个i,都会有一个线性方程:
f[i]=f[j1]/degree[i]+(1f[j2])/degree[i]+......
然后预处理出矩阵来,高斯消元即可。
代码:

#include<cstdio>#include<cmath>#include<algorithm>#include<cstring>#define ll long longusing namespace std;inline int read(){    int x=0;char ch=' ';int f=1;    while(ch<'0'||ch>'9')ch=getchar();    if(ch=='-'){        f=-1;ch=getchar();    }    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();    return x*f;}struct edge{    int to,next,w;}e[20001];int n,m,tot;int head[101];int in[101];double a[101][101];void gauss(){    for(int i=1;i<=n;i++){        int mx=i;        while(!a[mx][i])mx++;        if(i!=mx)swap(a[i],a[mx]);        double mul=a[i][i];        for(int j=i;j<=n+1;j++)a[i][j]/=mul;        for(int k=1;k<=n;k++){            if(k!=i&&a[k][i]){                mul=a[k][i];                for(int j=i;j<=n+1;j++){                    a[k][j]-=mul*a[i][j];                }            }        }    }}inline void addedge(int x,int y,int l){    e[++tot].to=y;e[tot].next=head[x];e[tot].w=l;head[x]=tot;in[y]++;}int main(){    n=read();m=read();    for(int i=1;i<=m;i++){        int x=read(),y=read(),l=read();        if(x!=y)addedge(x,y,l),addedge(y,x,l);        else addedge(x,y,l);    }    double ans=0;    for(int k=0;k<=30;k++){        memset(a,0,sizeof(a));        for(int x=1;x<n;x++){            a[x][x]=1.0;            for(int i=head[x];i;i=e[i].next){                int u=e[i].to;                if((e[i].w>>k)&1){                    a[x][u]+=1.0/in[x];a[x][n+1]+=1.0/in[x];                }                else{                    a[x][u]-=1.0/in[x];                }            }        }        a[n][n]=1.0;        gauss();        ans+=a[1][n+1]*1.0*(1<<k);    }    printf("%.3lf",ans);    return 0;}