多类型高斯消元杂题
- [SDOI2010]外星千足虫
- description
- solution
- code
- [HNOI2013]游走
- description
- solution
- code
- [HNOI2011]XOR和路径
- description
- solution
- code
- Maze(树上高斯消元)
- problem
- solution
- code
[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
将路径拆分成每条边期望经过次数乘以边权的求和
贪心的把最大权值放在期望经过次数最少的边上
但是边的级别是空间时间不足以承受的
事实上,边的期望至于边连接的两个端点有关
每个点到与之相连边的概率一样,期望一样
所以到某条特定边的期望就是经过该点期望除以该点连接的总边数
一条边被经过的期望则是两个端点到这条边的期望和
于是乎求边的期望就转化为求点的期望
而点的期望至于相邻点有关
设Ei:iE_i:iEi:i点期望,numi:inum_i:inumi:i点总边数,x1,x2,...,xkx_1,x_2,...,x_kx1,x2,...,xk与iii相连,则有Ei=∑j=1kEjnumjE_i=\sum_{j=1}^k\frac{E_j}{num_j}Ei=∑j=1knumjEj
每个点都能列出一个这样的方程,高斯消元解决
特别地,游走是从点111开始的,所以期望要+1+1+1,游走到点nnn就不会继续进行了,所以计算期望时不能纳入考虑
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
一般看到充满二进制意味的异或都要考虑拆位
此题也不例外,每一个单独计算期望
设fi:f_{i}:fi: 表示i→ni\rightarrow ni→n路径该位为111的概率,则1−fi1-f_i1−fi表示i→ni\rightarrow ni→n路径为000的概率,di:id_i:idi:i的出度
∀(u,v)∈Edgefu=1du(∑w(u,v)=0fv+∑w(u,v)=1(1−fv))⇔∀(u,v)∈Edgedu×fu=(∑w(u,v)=0fv+∑w(u,v)=1(1−fv))\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→nu\rightarrow nu→n的路径想要为1:1:1:如果(u→v)(u\rightarrow v)(u→v)这条边为111,必须v→nv\rightarrow nv→n的路径为000,否则为111
方程化为
dufu−∑w(u,v)=0fv+∑w(u,v)=1fu=∑w(u,v)=11d_uf_u-\sum_{w(u,v)=0}f_v+\sum_{w(u,v)=1}f_u=\sum_{w(u,v)=1}1dufu−∑w(u,v)=0fv+∑w(u,v)=1fu=∑w(u,v)=11
高斯消元,ans=∑i2ifi(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
令Ei:iE_i:iEi:i节点逃出期望经过的边数,edgei:iedge_i:iedgei:i相连边数
∀i,i∈leafEi=ki×E1+ei×0+(1−ki−ei)×(Efai+1)=ki×E1+(1−ki−ei)×Efai+(1−ki−ei)\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∉leafEi=ki×E1+ei×0+(1−ki−ei)×1edgei×(∑j∈soni(Ej+1)+Efai+1)=ki×E1+1−ki−eiedgei×Efai+1−ki−eim×∑j∈soniEj+(1−ki−ei)\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)
转移涉及父亲儿子且彼此依赖,而我们非常想得到不交叉的线性递推关系
想办法变成只跟父亲有关的转移,形式的设Ei=Ai×E1+Bi×Efai+CiE_i=A_i\times E_1+B_i\times E_{fa_i}+C_iEi=Ai×E1+Bi×Efai+Ci
类比解方程组,列出以下式子
∀i,i∉leaf\forall_{i,i\notin leaf}∀i,i∈/leaf
∑j∈soniEj=∑j∈soniAj×E1+Bj×Efaj+Cj=∑j∈soniAj×E1+Bj×Ei+Cj\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
Ei=kiE1+1−ki−eiedgeiEfai+1−ki−eim∑j∈soniEj+(1−ki−ei)=kiE1+1−ki−eiedgeiEfai+1−ki−eiedgei∑j∈soni(AjE1+BjEi+Cj)+(1−ki−ei)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−ki−eiedgei∑j∈soniBj)Ei=(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=(ki+1−ki−eiedgei∑j∈soniAj)E1+1−ki−eiedgeiEfai+(1−ki−ei)+1−ki−eiedgei∑j∈soniCj(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
⇒Ai=ki+1−ki−eiedgei∑j∈soniAj(1−1−ki−eiedgei∑j∈soniBj)\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∈soniAjBi=1−ki−eiedgei(1−1−ki−eiedgei∑j∈soniBj)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−eiCi=(1−ki−ei)+1−ki−eiedgei∑j∈soniCj(1−1−ki−eiedgei∑j∈soniBj)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∈leaf\forall_{i,i\in leaf}∀i,i∈leaf
Ei=ki×E1+(1−ki−ei)×Efai+(1−ki−ei)=Ai×E1+Bi×Efai+Ci⇒{Ai=kiBi=1−ki−eiCi=1−ki−eiE_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
从叶子节点开始倒着往上递推(也被称之为树上高斯消元)
树上高斯消元
把有关父亲和儿子的递推式通过Aix+Biy+CiA_ix+B_iy+C_iAix+Biy+Ci的形式转化为只由一方单向线性递推
然后正着/倒着递推消元
直到算出A1,B1,C1A_1,B_1,C_1A1,B1,C1
E1=A1×E1+B1×0+C1⇒E1=C11−A1E_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
当A1A_1A1无限趋近于111时,无解
如果iii的Ai,Bi,CiA_i,B_i,C_iAi,Bi,Ci的分母形式1−1−ki−eiedgei∑j∈soniBj1-\frac{1-k_i-e_i}{edge_i}\sum_{j\in son_i}B_j1−edgei1−ki−ei∑j∈soniBj趋近于000,也是无解
注意精度问题,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] ) );elseprintf( "Case %d: impossible\n", Case );}return 0;
}