2337: [HNOI2011]XOR和路径
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1294 Solved: 755
[ Submit][ Status][ Discuss]
Description
Input
Output
Sample Input
Sample Output
HINT
Source
这道题又给我打开了新的思路...
刚开始qjx讲这道题的时候我还觉得是普通的BZOJ3143游走那道题...但是转移的时候发现异或无法像经过次数等那么简单. 比如运算符号原来是+ 现在是^等, 转移方程就很难列出来了. 一般涉及到这类位运算符的都要考虑按位处理. 当然每次在算的时候, 比如考虑第p位, 我们只需要计算从1异或到n第p位还为1的概率是多少.
我们设f[i]为从i异或到n第p位为1的概率是多少. 那么(1<<p) * f[1]就是对答案的贡献.
若u, v连边, 这个边这一位为1的话, 那么我们定义是这一位为1, 如果异或这个1的话那么就没有了. 所以我们要(1-f[v])表示这一位为0的概率, 异或了之后就能转移到f[u]了, 如果为0的话就不用1-f[v]了. 因为是无向图, 没有拓扑关系, f数组都是未知的(f[n] = 0, 因为到了n就停止了), 所以我们会发现这是一个n-1行n-1列的方程, n不用算在内. 高斯消元即可. 但是注意如果u到n的边为1的话, 虽然不算n, 但是我们有1这个常数, 所以还是要算在a[u][n+1]即等式右边的头上.
#include<cmath>
#include<stdio.h>
#include<cstring>
#include<algorithm>
#define clear(a) memset(a, 0, sizeof(a))
using namespace std;
const int maxn = 105;
const double eps = 1e-10;
int n, m, num;
int d[maxn], pw[31], h[maxn];
double a[maxn][maxn], ans;
struct edge{ int nxt, v, w;}e[maxn * maxn * 2];
inline void add(int u, int v, int w){
e[++num].v = v, e[num].nxt = h[u], e[num].w = w, h[u] = num;
}
inline void gauss(){
int cnt = 0;
for(int i = 1; i <= n; ++i){
int j = -1;
for(int k = cnt + 1; k <= n; ++k)
if(fabs(a[k][i]) > fabs(a[j][i])) j = k;
if(j == -1) continue;
for(int k = i; k <= n + 1; ++k)
swap(a[j][k], a[cnt + 1][k]);
for(int j = 1; j <= n; ++j){
if(fabs(a[j][i]) < eps || j == cnt + 1) continue;
double r = a[j][i] / a[cnt + 1][i];
for(int k = i; k <= n + 1; ++k)
a[j][k] -= r * a[cnt + 1][k];
}
++cnt;
}
}
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++i){
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
add(u, v, w); d[u]++;
if(u ^ v) add(v, u, w), d[v]++;
}
--n;
pw[0] = 1;;
for(int i = 1; i <= 30; ++i) pw[i] = pw[i - 1] << 1;
for(int p = 0; p <= 30; ++p){
clear(a);
for(int u = 1; u <= n; ++u){
a[u][u] += d[u];
for(int i = h[u]; i; i = e[i].nxt){
int v = e[i].v, w = e[i].w;
if(v == n + 1){
if( w & pw[p] ) a[u][n + 1] += 1 ;
continue ;
}
if(w & pw[p]) a[u][v] += 1, a[u][n + 1] += 1;
else a[u][v] -= 1;
}
}
gauss();
ans += (a[1][n + 1] / a[1][1]) * pw[p];
}
printf("%0.3lf\n", ans);
}
/*
2 2
1 1 2
1 2 3
*/