1. 程式人生 > >bzoj 2337: [HNOI2011]XOR和路徑

bzoj 2337: [HNOI2011]XOR和路徑

bzoj long desc hnoi2011 處理 scan problem 發現 一次

Description

技術分享

Input

Output

Sample Input

Sample Output

HINT

Source

Day2

終於把這個史前遺留的坑給填了。。。

首先異或的話由位無關性,可以按位處理。。。

那麽對於每一位,設f[i]表示從i出發第一次到達n且xor和為1的概率,out[i]為i的出邊,那麽轉移就比較容易了。。。

if(w(i,j)&xxx) f[i]+=(1-f[j)/out[i];// 這條邊該位為1,需要xor上0,xor和才為1

else f[i]+=f[j]/out[i];//同上。。。

但是這個有環,而且可以走重邊自環,肯定是不能dp的,

但是我們發現對於每個f[i]=f[j]/out[i]+(1-f[j‘]/out[i])...都是一個線性方程。。。所以這是一個線性方程組。。。

然後我們由已知f[n]=1,所以可以用高斯消元解決。。。很妙啊。。。

// MADE BY QT666
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=150;
const int M=200050;
int out[N];
int head[M],nxt[M],to[M],cnt,w[M],n,m;
double a[N][N],ans;
void gauss() {
  for(int i=1;i<=n;i++) {
    int t=i;
    while(!a[t][i]) t++;
    if(i!=t) swap(a[t],a[i]);
    double k=a[i][i];
    for(int j=i;j<=n+1;j++) a[i][j]/=k;
    for(int j=1;j<=n;j++)
      if(j!=i&&a[j][i]) {
    k=a[j][i];
    for(int p=i;p<=n+1;p++) a[j][p]-=k*a[i][p];
      }
  }
}
void lnk(int x,int y,int z){
  to[++cnt]=y,nxt[cnt]=head[x],w[cnt]=z,head[x]=cnt;
}
int main(){
  scanf("%d%d",&n,&m);
  for(int i=1;i<=m;i++){
    int u,v,val;scanf("%d%d%d",&u,&v,&val);
    lnk(u,v,val);out[u]++;
    if(u!=v) out[v]++,lnk(v,u,val);
  }
  int gg;
  for(int k=0;k<=30;k++){
    if(k==0) gg=1;else gg=gg<<1;
    memset(a,0,sizeof(a));
    for(int i=1;i<n;i++){
      a[i][i]=-1.0;
      for(int j=head[i];j;j=nxt[j]){
    int y=to[j];
    if(w[j]&gg){
      a[i][y]-=1.0/out[i],a[i][n+1]-=1.0/out[i];
    }
    else a[i][y]+=1.0/out[i];
      }
    }
    a[n][n]=-1.0;gauss();ans+=a[1][n+1]*gg;
  }
  printf("%.3f\n",ans);
  return 0;
}

bzoj 2337: [HNOI2011]XOR和路徑