多类型高斯消元杂题
[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 i→n路径该位为 1 1 1的概率,则 1 − f i 1-f_i 1−fi表示 i → n i\rightarrow n i→n路径为 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(1−fv))⇔∀(u,v)∈Edgedu×fu=(∑w(u,v)=0fv+∑w(u,v)=1(1−fv))
换言之, u → n u\rightarrow n u→n的路径想要为 1 : 1: 1:如果 ( u → v ) (u\rightarrow v) (u→v)这条边为 1 1 1,必须 v → n v\rightarrow n v→n的路径为 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 dufu−∑w(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,i∈leafEi=ki×E1+ei×0+(1−ki−ei)×(Efai+1)=ki×E1+(1−ki−ei)×Efai+(1−ki−ei)
∀ 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+(1−ki−ei)×edgei1×(j∈soni∑(Ej+1)+Efai+1)=ki×E1+edgei1−ki−ei×Efai+m1−ki−ei×j∈soni∑Ej+(1−ki−ei)
转移涉及父亲儿子且彼此依赖,而我们非常想得到不交叉的线性递推关系
想办法变成只跟父亲有关的转移,形式的设 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\\
j∈soni∑Ej=j∈soni∑Aj×E1+Bj×Efaj+Cj=j∈soni∑Aj×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+edgei1−ki−eiEfai+m1−ki−eij∈soni∑Ej+(1−ki−ei)=kiE1+edgei1−ki−eiEfai+edgei1−ki−eij∈soni∑(AjE1+BjEi+Cj)+(1−ki−ei)
⇔
\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=
(1−edgei1−ki−eij∈soni∑Bj)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+edgei1−ki−eij∈soni∑Aj)E1+edgei1−ki−eiEfai+(1−ki−ei)+edgei1−ki−eij∈soni∑Cj
⇒
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=(1−edgei1−ki−ei∑j∈soniBj)ki+edgei1−ki−ei∑j∈soniAj
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=(1−edgei1−ki−ei∑j∈soniBj)edgei1−ki−ei
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=(1−edgei1−ki−ei∑j∈soniBj)(1−ki−ei)+edgei1−ki−ei∑j∈soniCj
∀
i
,
i
∈
l
e
a
f
\forall_{i,i\in leaf}
∀i,i∈leaf
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+(1−ki−ei)×Efai+(1−ki−ei)=Ai×E1+Bi×Efai+Ci⇒⎩⎪⎨⎪⎧Ai=kiBi=1−ki−eiCi=1−ki−ei
从叶子节点开始倒着往上递推(也被称之为树上高斯消元)
树上高斯消元
把有关父亲和儿子的递推式通过 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+C1⇒E1=1−A1C1
当 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 1−edgei1−ki−ei∑j∈soniBj趋近于 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;
}