[BZOJ2337] [HNOI2011] XOR和路径 期望 + 按位处理 + 高斯消元

2337: [HNOI2011]XOR和路径

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 1294   Solved: 755
[ Submit][ Status][ Discuss]

Description

Input

Output

Sample Input

Sample Output

HINT

Source

[ Submit][ Status][ Discuss]


HOME Back

这道题又给我打开了新的思路...

刚开始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
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值