YBROJ&洛谷P3211:XOR和路径(线性基,期望dp)

博客探讨了一种利用图论和概率计算的方法,解决从起点到终点路径异或和为特定值的概率问题。通过反向思考,定义从任意点出发到达目标点路径异或和为1的概率,并使用高斯消元法求解。内容涉及动态规划、图的遍历和概率计算,适合对算法和数学感兴趣的读者。

解析

不难想到第一步利用期望线性性逐位考虑。
然后就变成求一个布尔变量的期望了,可以直接转化为求概率。

我一开始的想求从1出发异或和为0/1的概率,然而这个东西在原点1附近的转移特别别扭…老出现概率大于1的迷惑情况。
然后我就不会了

正解是反过来想,设 fxf_xfx 为从 xxx 出发到 nnn 的路径异或和为1的概率,然后像类似游走的方法转移就行了。

为什么要这样?
首先,本题的图必然联通,此时,从一个点 xxx 必然可以走到 nnn,但从 111 不一定能走到 xxx
比如说 1-3-2 这样的图中,1就是走不到2的。
那么在题解的定义中,1−fx1-f_x1fx 就使 x−>nx->nx>n 的路径异或和为 000 的概率,而在我的定义中,1−fx1-f_x1fx 则是 111 走不到 xxx1−>x1->x1>x 路径异或和为0的概率和。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define OK debug("OK\n")
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}
const int N=100+20;
const int mod=1e9+7;
int n,m;
struct node{
  int to,nxt,w;
}p[N*N<<1];
int fi[N],cnt;
inline void addline(int x,int y,int w){
  p[++cnt]=(node){y,fi[x],w};fi[x]=cnt;
}
int mi[40],o=30;
double a[N][N],ans,f[N];
int d[N];
void gauss(){
  for(int i=1;i<=n;i++){
    int id=i;
    for(int j=i+1;j<=n;j++) if(abs(a[j][i])>abs(a[id][i])) id=j;
    if(id!=i) swap(a[id],a[i]);
    for(int j=i+1;j<=n;j++){
      for(int k=i+1;k<=n+1;k++) a[j][k]-=a[j][i]/a[i][i]*a[i][k];
    }
  }
  for(int i=n;i>=1;i--){
    f[i]=a[i][n+1]/a[i][i];
    for(int j=1;j<i;j++) a[j][n+1]-=a[j][i]*f[i];
  }
  return;
}
void work(int k){
  memset(a,0,sizeof(a));
  for(int i=1;i<=n;i++){
    a[i][i]=1;
    if(i==n) continue;
    for(int o=fi[i];~o;o=p[o].nxt){
      int j=p[o].to;
      int op=(p[o].w&mi[k])?1:0;
      if(op){
	a[i][n+1]+=1.0/d[i];
	a[i][j]+=1.0/d[i];
      }
      else{
	a[i][j]+=-1.0/d[i];
      }
    }
  }
  //for(int i=1;i<=n;i++){
  //for(int j=1;j<=n+1;j++) printf("%.2lf ",a[i][j]);
  //puts("");
  //}
  gauss();
  ans+=mi[k]*f[1];
//printf("k=%d f=%lf\n",k,f[1]);
}

signed main(){
#ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
#endif
  memset(fi,-1,sizeof(fi));cnt=-1;
  mi[0]=1;
  for(int i=1;i<=o;i++) mi[i]=mi[i-1]<<1;
  n=read();m=read();
  for(int i=1;i<=m;i++){
    int x=read(),y=read(),w=read();
    d[x]++;addline(x,y,w);
    if(x^y) d[y]++,addline(y,x,w);
  }
  for(int i=0;i<=o;i++) work(i);
  printf("%.3lf\n",ans);
}
/*
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值