线性代数五之高斯消元——[SDOI2010]外星千足虫,[HNOI2013]游走,[HNOI2011]XOR和路径,[hdu 4035]Maze

[SDOI2010]外星千足虫

description

solution

高斯消元的模板题

虽然感觉问了个最早确定所有虫的时间戳,但并没有什么用

在高斯消元过程中,使用的最大行编号就是最早确定时间

code

#include <cstdio>
#include <bitset>
#include <iostream>
using namespace std;
#define maxn 1005
int n, m;
bitset < maxn > a[maxn << 1];

int main() {
	scanf( "%d %d", &n, &m );
	for( int i = 1, x;i <= m;i ++ ) {
		for( int j = 1;j <= n;j ++ )
			scanf( "%1d", &x ), a[i][j] = x;
		scanf( "%d", &x ), a[i][n + 1] = x;
	}
	int ans = 0;
	for( int i = 1;i <= n;i ++ ) {
		int row = i;
		while( row <= m && ! a[row][i] ) row ++;
		if( row == m + 1 ) return ! printf( "Cannot Determine\n" );
		if( i ^ row ) swap( a[row], a[i] );
		ans = max( ans, row );
		for( int j = 1;j <= m;j ++ )
			if( i == j || ! a[j][i] ) continue;
			else a[j] ^= a[i];
	}
	printf( "%d\n", ans );
	for( int i = 1;i <= n;i ++ )
		if( a[i][n + 1] ) printf( "?y7M#\n" );
		else printf( "Earth\n" );
	return 0;
}

[HNOI2013]游走

description

solution

将路径拆分成每条边期望经过次数乘以边权的求和

贪心的把最大权值放在期望经过次数最少的边上

但是边的级别是空间时间不足以承受的

事实上,边的期望至于边连接的两个端点有关

每个点到与之相连边的概率一样,期望一样

所以到某条特定边的期望就是经过该点期望除以该点连接的总边数

一条边被经过的期望则是两个端点到这条边的期望和

于是乎求边的期望就转化为求点的期望

而点的期望至于相邻点有关

E i : i E_i:i Ei:i点期望, n u m i : i num_i:i numi:i点总边数, x 1 , x 2 , . . . , x k x_1,x_2,...,x_k x1,x2,...,xk i i i相连,则有 E i = ∑ j = 1 k E j n u m j E_i=\sum_{j=1}^k\frac{E_j}{num_j} Ei=j=1knumjEj

每个点都能列出一个这样的方程,高斯消元解决

特别地,游走是从点 1 1 1开始的,所以期望要 + 1 +1 +1,游走到点 n n n就不会继续进行了,所以计算期望时不能纳入考虑

code

#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 505
#define eps 1e-6
#define maxm 125005
vector < int > G[maxn]; 
int n, m;
int num[maxn], from[maxm], to[maxm];
double E[maxm];
double x[maxn][maxn];

double Fabs( double x ) {
	return x < 0 ? -x : x;
}

int main() {
	scanf( "%d %d", &n, &m );
	for( int i = 1, u, v;i <= m;i ++ ) {
		scanf( "%d %d", &u, &v );
		G[u].push_back( v );
		G[v].push_back( u );
		from[i] = u, to[i] = v;
		num[u] ++, num[v] ++;
	}
	x[1][n] = 1;
	for( int i = 1;i < n;i ++ ) {
		x[i][i] = 1;
		for( auto j : G[i] )
			if( j != n )
				x[i][j] = -1.0 / num[j];
	}
	for( int i = 1;i < n;i ++ ) {
		int k = i;
		for( int j = i + 1;j < n;j ++ )
			if( Fabs( x[j][i] ) > Fabs( x[k][i] ) )
				k = j;
		if( i ^ k ) swap( x[i], x[k] );
		for( int j = n;j >= i;j -- )
			x[i][j] /= x[i][i];
		for( int j = 1;j < n;j ++ )
			if( i ^ j )
				for( int k = n;k >= i;k -- )
					x[j][k] -= x[j][i] * x[i][k];
	}
	for( int i = 1;i <= m;i ++ ) {
		if( from[i] != n )
			E[i] += x[from[i]][n] / num[from[i]];
		if( to[i] != n )
			E[i] += x[to[i]][n] / num[to[i]];
	}
	sort( E + 1, E + m + 1 );
	double ans = 0;
	for( int i = 1;i <= m;i ++ )
		ans += E[i] * ( m - i + 1 );
	printf( "%.3f", ans );
	return 0;
} 

[HNOI2011]XOR和路径

description

solution

一般看到充满二进制意味的异或都要考虑拆位

此题也不例外,每一个单独计算期望

f i : f_{i}: fi: 表示 i → n i\rightarrow n in路径该位为 1 1 1的概率,则 1 − f i 1-f_i 1fi表示 i → n i\rightarrow n in路径为 0 0 0的概率, d i : i d_i:i di:i的出度

∀ ( u , v ) ∈ E d g e f u = 1 d u ( ∑ w ( u , v ) = 0 f v + ∑ w ( u , v ) = 1 ( 1 − f v ) ) ⇔ ∀ ( u , v ) ∈ E d g e d u × f u = ( ∑ w ( u , v ) = 0 f v + ∑ w ( u , v ) = 1 ( 1 − f v ) ) \forall_{(u,v)\in Edge}f_u=\frac{1}{d_u}\Big(\sum_{w(u,v)=0}f_v+\sum_{w(u,v)=1}(1-f_v)\Big)\Leftrightarrow \forall_{(u,v)\in Edge}d_u\times f_u=\Big(\sum_{w(u,v)=0}f_v+\sum_{w(u,v)=1}(1-f_v)\Big) (u,v)Edgefu=du1(w(u,v)=0fv+w(u,v)=1(1fv))(u,v)Edgedu×fu=(w(u,v)=0fv+w(u,v)=1(1fv))
在这里插入图片描述

换言之, u → n u\rightarrow n un的路径想要为 1 : 1: 1:如果 ( u → v ) (u\rightarrow v) (uv)这条边为 1 1 1,必须 v → n v\rightarrow n vn的路径为 0 0 0,否则为 1 1 1

方程化为

d u f u − ∑ w ( u , v ) = 0 f v + ∑ w ( u , v ) = 1 f u = ∑ w ( u , v ) = 1 1 d_uf_u-\sum_{w(u,v)=0}f_v+\sum_{w(u,v)=1}f_u=\sum_{w(u,v)=1}1 dufuw(u,v)=0fv+w(u,v)=1fu=w(u,v)=11

高斯消元, a n s = ∑ i 2 i f i ( 1 ) ans=\sum_i2^if_i(1) ans=i2ifi(1)

code

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
#define eps 1e-8
#define maxn 105
vector < pair < int, int > > G[maxn];
int n, m;
int d[maxn];
double ans[maxn];
double a[maxn][maxn];

double Fabs( double x ) {
	return x < 0 ? -x : x;
}

void build( int x ) {
	a[n][n] = 1;
	for( int i = 1;i < n;i ++ ) {
		a[i][i] = d[i];
		for( auto j : G[i] ) {
			if( j.second & x ) a[i][j.first] ++, a[i][n + 1] ++;
			else a[i][j.first] --;		
		}
	}
}

void gauss() {
	for( int i = 1;i <= n;i ++ ) {
		int k = i;
		for( int j = i;j <= n;j ++ )
			if( Fabs( a[j][i] ) > Fabs( a[k][i] ) )
				k = j;
		if( k ^ i ) swap( a[i], a[k] );
		for( int j = i + 1;j <= n;j ++ )
			if( Fabs( a[j][i] ) < eps ) continue;
			else {
				double t = a[j][i] / a[i][i];
				for( k = i;k <= n + 1;k ++ )
					a[j][k] -= a[i][k] * t;
				a[j][i] = 0;
			}
	}
	for( int i = n;i;i -- ) {
		for( int j = i + 1;j <= n;j ++ )
			a[i][n + 1] -= a[i][j] * ans[j];
		ans[i] = a[i][n + 1] / a[i][i];
	}
	memset( a, 0, sizeof( a ) );
}

int main() {
	scanf( "%d %d", &n, &m );
	int maxx = 0;
	for( int i = 1, u, v, w;i <= m;i ++ ) {
		scanf( "%d %d %d", &u, &v, &w );
		G[u].push_back( make_pair( v, w ) );
		d[u] ++;
		if( u ^ v ) {
			G[v].push_back( make_pair( u, w ) );
			d[v] ++;
		}
		maxx = max( maxx, w );
	}
	double ret = 0;
	for( int i = 1;i <= maxx;i <<= 1 ) {
		build( i );
		gauss();
		ret += ans[1] * i;
	}
	printf( "%.3f\n", ret );
	return 0;
} 

Maze(树上高斯消元)

problem

solution

E i : i E_i:i Ei:i节点逃出期望经过的边数, e d g e i : i edge_i:i edgei:i相连边数
∀ i , i ∈ l e a f E i = k i × E 1 + e i × 0 + ( 1 − k i − e i ) × ( E f a i + 1 ) = k i × E 1 + ( 1 − k i − e i ) × E f a i + ( 1 − k i − e i ) \forall_{i,i\in leaf}E_i=k_i\times E_1+e_i\times 0+(1-k_i-e_i)\times (E_{fa_i}+1)\\=k_i\times E_1+(1-k_i-e_i)\times E_{fa_i}+(1-k_i-e_i) i,ileafEi=ki×E1+ei×0+(1kiei)×(Efai+1)=ki×E1+(1kiei)×Efai+(1kiei)

∀ i , i ∉ l e a f E i = k i × E 1 + e i × 0 + ( 1 − k i − e i ) × 1 e d g e i × ( ∑ j ∈ s o n i ( E j + 1 ) + E f a i + 1 ) = k i × E 1 + 1 − k i − e i e d g e i × E f a i + 1 − k i − e i m × ∑ j ∈ s o n i E j + ( 1 − k i − e i ) \forall_{i,i\notin leaf}E_{i}=k_i\times E_1+e_i\times 0+(1-k_i-e_i)\times\frac{1}{edge_i}\times\Big(\sum_{j\in son_i}(E_j+1)+E_{fa_i}+1\Big)\\=k_i\times E_1+\frac{1-k_i-e_i}{edge_i}\times E_{fa_i}+\frac{1-k_i-e_i}{m}\times\sum_{j\in son_i}E_j+(1-k_i-e_i) i,i/leafEi=ki×E1+ei×0+(1kiei)×edgei1×(jsoni(Ej+1)+Efai+1)=ki×E1+edgei1kiei×Efai+m1kiei×jsoniEj+(1kiei)

转移涉及父亲儿子且彼此依赖,而我们非常想得到不交叉的线性递推关系

想办法变成只跟父亲有关的转移,形式的设 E i = A i × E 1 + B i × E f a i + C i E_i=A_i\times E_1+B_i\times E_{fa_i}+C_i Ei=Ai×E1+Bi×Efai+Ci

类比解方程组,列出以下式子

∀ i , i ∉ l e a f \forall_{i,i\notin leaf} i,i/leaf
∑ j ∈ s o n i E j = ∑ j ∈ s o n i A j × E 1 + B j × E f a j + C j = ∑ j ∈ s o n i A j × E 1 + B j × E i + C j \sum_{j\in son_i}E_j=\sum_{j\in son_i}A_j\times E_1+B_j\times E_{fa_j}+C_j=\sum_{j\in son_i}A_j\times E_1+B_j\times E_i+C_j\\ jsoniEj=jsoniAj×E1+Bj×Efaj+Cj=jsoniAj×E1+Bj×Ei+Cj

E i = k i E 1 + 1 − k i − e i e d g e i E f a i + 1 − k i − e i m ∑ j ∈ s o n i E j + ( 1 − k i − e i ) = k i E 1 + 1 − k i − e i e d g e i E f a i + 1 − k i − e i e d g e i ∑ j ∈ s o n i ( A j E 1 + B j E i + C j ) + ( 1 − k i − e i ) E_i=k_iE_1+\frac{1-k_i-e_i}{edge_i}E_{fa_i}+\frac{1-k_i-e_i}{m}\sum_{j\in son_i}E_j+(1-k_i-e_i)\\=k_iE_1+\frac{1-k_i-e_i}{edge_i}E_{fa_i}+\frac{1-k_i-e_i}{edge_i}\sum_{j\in son_i}(A_jE_1+B_jE_i+C_j)+(1-k_i-e_i) Ei=kiE1+edgei1kieiEfai+m1kieijsoniEj+(1kiei)=kiE1+edgei1kieiEfai+edgei1kieijsoni(AjE1+BjEi+Cj)+(1kiei)
在这里插入图片描述

⇔ \Leftrightarrow
( 1 − 1 − k i − e i e d g e i ∑ j ∈ s o n i B j ) E i = (1-\frac{1-k_i-e_i}{edge_i}\sum_{j\in son_i}B_j)E_i= (1edgei1kieijsoniBj)Ei= ( k i + 1 − k i − e i e d g e i ∑ j ∈ s o n i A j ) E 1 + 1 − k i − e i e d g e i E f a i + ( 1 − k i − e i ) + 1 − k i − e i e d g e i ∑ j ∈ s o n i C j (k_i+\frac{1-k_i-e_i}{edge_i}\sum_{j\in son_i}A_j)E_1+\frac{1-k_i-e_i}{edge_i}E_{fa_i}+(1-k_i-e_i)+\frac{1-k_i-e_i}{edge_i}\sum_{j\in son_i}C_j (ki+edgei1kieijsoniAj)E1+edgei1kieiEfai+(1kiei)+edgei1kieijsoniCj
⇒ A i = k i + 1 − k i − e i e d g e i ∑ j ∈ s o n i A j ( 1 − 1 − k i − e i e d g e i ∑ j ∈ s o n i B j ) \Rightarrow A_i=\frac{k_i+\frac{1-k_i-e_i}{edge_i}\sum_{j\in son_i}A_j}{(1-\frac{1-k_i-e_i}{edge_i}\sum_{j\in son_i}B_j)} Ai=(1edgei1kieijsoniBj)ki+edgei1kieijsoniAj B i = 1 − k i − e i e d g e i ( 1 − 1 − k i − e i e d g e i ∑ j ∈ s o n i B j ) B_i=\frac{\frac{1-k_i-e_i}{edge_i}}{(1-\frac{1-k_i-e_i}{edge_i}\sum_{j\in son_i}B_j)} Bi=(1edgei1kieijsoniBj)edgei1kiei C i = ( 1 − k i − e i ) + 1 − k i − e i e d g e i ∑ j ∈ s o n i C j ( 1 − 1 − k i − e i e d g e i ∑ j ∈ s o n i B j ) C_i=\frac{(1-k_i-e_i)+\frac{1-k_i-e_i}{edge_i}\sum_{j\in son_i}C_j}{(1-\frac{1-k_i-e_i}{edge_i}\sum_{j\in son_i}B_j)} Ci=(1edgei1kieijsoniBj)(1kiei)+edgei1kieijsoniCj
在这里插入图片描述

∀ i , i ∈ l e a f \forall_{i,i\in leaf} i,ileaf
E i = k i × E 1 + ( 1 − k i − e i ) × E f a i + ( 1 − k i − e i ) = A i × E 1 + B i × E f a i + C i ⇒ { A i = k i B i = 1 − k i − e i C i = 1 − k i − e i E_i=k_i\times E_1+(1-k_i-e_i)\times E_{fa_i}+(1-k_i-e_i)=A_i\times E_1+B_i\times E_{fa_i}+C_i\\ \Rightarrow \begin{cases}A_i=k_i\\B_i=1-k_i-e_i\\C_i=1-k_i-e_i\end{cases} Ei=ki×E1+(1kiei)×Efai+(1kiei)=Ai×E1+Bi×Efai+CiAi=kiBi=1kieiCi=1kiei
从叶子节点开始倒着往上递推(也被称之为树上高斯消元)

树上高斯消元

把有关父亲和儿子的递推式通过 A i x + B i y + C i A_ix+B_iy+C_i Aix+Biy+Ci的形式转化为只由一方单向线性递推

然后正着/倒着递推消元

直到算出 A 1 , B 1 , C 1 A_1,B_1,C_1 A1,B1,C1

E 1 = A 1 × E 1 + B 1 × 0 + C 1 ⇒ E 1 = C 1 1 − A 1 E_1=A_1\times E_1+B_1\times 0+C_1\Rightarrow E_1=\frac{C_1}{1-A_1} E1=A1×E1+B1×0+C1E1=1A1C1

A 1 A_1 A1无限趋近于 1 1 1时,无解

如果 i i i A i , B i , C i A_i,B_i,C_i Ai,Bi,Ci的分母形式 1 − 1 − k i − e i e d g e i ∑ j ∈ s o n i B j 1-\frac{1-k_i-e_i}{edge_i}\sum_{j\in son_i}B_j 1edgei1kieijsoniBj趋近于 0 0 0,也是无解

注意精度问题,eps=1e-8都不行,因为涉及 / 100 /100 /100

code

#include <cmath>
#include <cstdio>
#include <vector>
using namespace std;
#define eps 1e-10
#define maxn 10005
vector < int > G[maxn];
int T, n;
double k[maxn], e[maxn], A[maxn], B[maxn], C[maxn];

bool dfs( int i, int fa ) {
	if( G[i].size() == 1 && G[i][0] == fa ) {
		A[i] = k[i], B[i] = C[i] = 1 - k[i] - e[i];
		return 1;
	}
	A[i] = k[i];
	B[i] = ( 1 - k[i] - e[i] ) / G[i].size();
	C[i] = 1 - k[i] - e[i];
	double t = 0;
	for( auto j : G[i] ) {
		if( j == fa ) continue;
		else if( ! dfs( j, i ) ) return 0;
		A[i] += A[j] * B[i];
		C[i] += C[j] * B[i];
		t += B[j] * B[i];
	}
	if( fabs( 1 - t ) < eps ) return 0;
	else {
		A[i] /= ( 1 - t );
		B[i] /= ( 1 - t );
		C[i] /= ( 1 - t );
		return 1;
	}
}

int main() {
	scanf( "%d", &T );
	for( int Case = 1;Case <= T;Case ++ ) {
		scanf( "%d", &n );
		for( int i = 1;i <= n;i ++ )
			G[i].clear();
		for( int i = 1, u, v;i < n;i ++ ) {
			scanf( "%d %d", &u, &v );
			G[u].push_back( v );
			G[v].push_back( u );  
		}
		for( int i = 1;i <= n;i ++ ) {
			scanf( "%lf %lf", &k[i], &e[i] );
			k[i] /= 100, e[i] /= 100;
		}
		if( dfs( 1, 0 ) && fabs( 1 - A[1] ) > eps )
			printf( "Case %d: %f\n", Case, C[1] / ( 1 - A[1] ) );
		else
			printf( "Case %d: impossible\n", Case );
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值