题意描述:有n个星球,m台望远镜。每台望远镜有一个开始时间和结束时间,但只给出了月、日的信息,没有给出年份,每台望远镜记录了它所观测的星球上发生的各类事件的次数。每类事件持续的时间是恒定的,且不会超过365天,不管在哪个星球上发生。告诉你每台望远镜的起止时间,和它观测到的各类时间发生的次数。问每类事件持续多长时间?可能有多个解,输出一个可行解即可。
数据范围:n<200,m<200
分析:设第i类时间持续的时间为xi,第i个望远镜的观测时间长度为Ci,它观测到的各类事件发生的次数为fi,则第i个望远镜可以列出以下方程:
∑fi*xi%365=Ci
于是可以得到一个模线性方程组。但是因为365不是质数,不能直接利用高斯-亚当消元法解这个方程组。为什么呢?
因为高斯亚当消元要对方程进行线性变换,比如乘上某个系数。但是乘的系数与模不互质的话,则变换前后两个方程不是等价的,可能造成解集扩大。
比如方程A:2x%15=1
方程A乘上系数3,得到方程B: 6x%15=3。
方程A和B不是等价的。满足A的解一定满足B,但是满足B的解不一定满足A。或者说方程A可以得到B,但方程B不能得到A。
所以对模型线性方程组要进行线性变换一定要保证模是质数。
那么接下来怎么做呢?对365进行质因数分解:365=73*5.
于是由方程∑fi*xi%365=Ci可以得到两个方程:
∑fi*xi%73=Ci%73
∑fi*xi%5=Ci%5
于是原来的方程组可以化成两个方程组。对其分别求解,设方程1的解集为X1,方程2的解集为X2。
则可以由X1和X2构造出一个解X,同时满足两个方程,即利用中国剩余定理.
代码如下:
/* Sluzbeno rjesenje zadatka Planete. Trebalo bi dobiti 100% bodova. Slozenost algoritma: O( N^2*F ) Autor: Goran Zuzic */ #include <algorithm> #include <functional> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <vector> #include <string> using namespace std; const int MAXN = 210; const int MAXF = 210; int N, F; int DuM[ 12 ] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int Init[ MAXN ][ MAXF+1 ]; int Mat[ MAXN ][ MAXF+1 ]; inline int get_day() { int d, m; scanf( "%d %d", &d, &m ); --d, --m; for( int i = 0; i < m; ++i ) d += DuM[i]; return d; } int Sol5[ MAXF ]; int Sol73[ MAXF ]; int Inverz[ 100 ]; int superOk = true; void gauss_solve( int p, int *Sol ) { for( int i = 0; i < N; ++i ) for( int j = 0; j <= F; ++j ) Mat[i][j] = Init[i][j] % p; Inverz[0] = 0; for( int i = 1; i < p; ++i ) for( int j = 1; j < p; ++j ) if( (i*j)%p == 1 ) Inverz[i] = j; int R = 0; for( int s = 0; s < F; ++s ) { int indeks = -1; for( int i = R; indeks == -1 && i < N; ++i ) if( Mat[i][s] != 0 ) indeks = i; //indeks是该列不为0的所有行的第一行 if( indeks == -1 ) continue;//该列为0 if( R != indeks )//第R行和第i行交换 for( int i = 0; i <= F; ++i ) swap( Mat[R][i], Mat[indeks][i] ); int mnozi = Inverz[ Mat[R][s] ];//求系数的逆元,作为倍数。 for( int i = 0; i <= F; ++i ) Mat[R][i] = ( Mat[R][i]*mnozi ) % p; for( int i = 0; i < N; ++i ) if( i != R ) { int coef = Mat[i][s]; for( int j = 0; j <= F; ++j ) { Mat[i][j] -= coef * Mat[R][j]; Mat[i][j] %= p; if( Mat[i][j] < 0 ) Mat[i][j] += p; } } ++R; } for( int i = 0; i < N; ++i ) { int first = -1; for( int j = 0; j < F; ++j ) if( Mat[i][j] != 0 ) { first = j; break; } if( first == -1 ) { if( Mat[i][F] != 0 ) { superOk = false; return ; } continue; } Sol[ first ] = Mat[i][F]; } } int main( void ) { scanf( "%d %d", &N, &F ); for( int i = 0; i < N; ++i ) { int a = get_day(); int b = get_day(); for( int j = 0; j < F; ++j ) scanf( "%d", Init[i] + j ); Init[i][F] = ((b-a)%365+365)%365; } gauss_solve( 5, Sol5 ); gauss_solve( 73, Sol73 ); if( !superOk ) { printf( "-1 " ); return 0; } for( int i = 0; i < F; ++i ) { int tmp = ( 146*Sol5[i] + 220*Sol73[i] ) % 365; if( tmp == 0 ) tmp = 365; printf( "%d ", tmp ); } return 0; }