[HNOI2011] XOR和路径

Description

给定一个\(N(N\leq 100)\)个点的无向连通图,边有边权。行动的规则是随机选择与当前点相连的一条边,移动到这条边的另一个顶点。求从\(1\)\(n\)的经过的路径上边权的期望\(XOR\)和。

Solution

跟两年后的‘游走’挺像的?这大概是一类套路?

显然的状态是\(f[i]\)表示从\(i\)\(n\)的期望\(XOR\)和。

跟那个题不一样的是这里是求\(XOR\)和,因为它有重边不是很容易高斯消元,我们考虑拆位算这个东西。

拆了位问题就很简单了。假设当前位数为\(bit\)

枚举\(i\)的出边,设另一个顶点是\(j\)。如果这条边的边权的\(bit\)位是\(1\),那么\(f[i]+=(1-f[j])\),否则\(f[i]+=f[j]\) 。这样资瓷合并,高斯消元套上去就完事了。

Code

#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<cctype>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using std::min;
using std::max;
using std::swap;
using std::vector;
const int N=105;
typedef double db;
typedef long long ll;
#define pb(A) push_back(A)
#define pii std::pair<int,int>
#define all(A) A.begin(),A.end()
#define mp(A,B) std::make_pair(A,B)

db a[N][N],f[N],ans;
int n,m,cnt,head[N],deg[N];

struct Edge{
    int to,nxt,dis;
}edge[N*200];

void add(int x,int y,int z){
    edge[++cnt].to=y;
    edge[cnt].nxt=head[x];
    edge[cnt].dis=z;
    head[x]=cnt;
}

int getint(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch))w|=ch=='-',ch=getchar();
    while( isdigit(ch))X=X*10+ch-48,ch=getchar();
    if(w) return -X;return X;
}

db abs(db x){
    return x>0?x:-x;
}

void print(){
    for(int i=1;i<=n;i++,puts(""))
        for(int j=1;j<=n+1;j++)
            printf("%.3lf ",a[i][j]);
}

db calc(int bit){
    memset(a,0,sizeof a);memset(f,0,sizeof f);
    for(int i=1;i<n;i++){
        a[i][i]=-deg[i];
        for(int j=head[i];j;j=edge[j].nxt){
            int to=edge[j].to;
            if(to==n){
                if(edge[j].dis>>bit&1) a[i][n]--;
            } else{
                if(edge[j].dis>>bit&1) a[i][to]--,a[i][n]--;
                else a[i][to]++;
            }
        }
    }
    for(int i=1;i<n;i++){
        int idx=i;
        for(int j=i+1;j<n;j++){
            if(abs(a[j][i])-abs(a[idx][i])>1e-8)
                idx=j;
        } swap(a[i],a[idx]);
        for(int j=i+1;j<n;j++){
            db tmp=a[j][i]/a[i][i];
            for(int p=i;p<=n;p++)
                a[j][p]-=tmp*a[i][p];
        }
    }
    for(int i=n-1;i;i--){
        for(int j=i+1;j<n;j++)
            a[i][n]-=a[i][j]*f[j];
        f[i]=a[i][n]/a[i][i];
    } return f[1];
}

signed main(){
    n=getint(),m=getint();
    for(int i=1;i<=m;i++){
        int x=getint(),y=getint(),z=getint();
        add(x,y,z);deg[x]++;
        if(x!=y) add(y,x,z),deg[y]++;
    }
    for(int i=0;i<=30;i++)
        ans+=calc(i)*(1<<i);
    printf("%.3lf\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/YoungNeal/p/9848133.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值