题意
求一个数N,给出C和S,表示有C个条件,每个条件有X 和 k,然后是该个条件的k个yi,即NmodX=yj,输出满足的最小的S个N,要求正整数。
思路
计算total为一共能有多少种解,
total小则用CRT求出所有组合的解
total数大的时候,可以通过枚举N来判断,找到一组k/x最小的最为枚举基准,然后判断即可
代码
//uva11754
//求一组模方程的解
// http://blog.miskcoo.com/2014/09/chinese-remainder-theorem#i-4
// http://www.voidcn.com/article/p-mlizjmuk-nc.html
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <set>
#include <vector>
using namespace std;
typedef long long LL;
const int maxc = 15;//一共C个方程
const int maxk = 105;//每个方程可能的余数有k个
const int limit = 10000;
LL total;
int C, S; // clues, desired sol
int bestc;
int prime[ maxc ], k[ maxc ]; //prime储存模数 // sol % prime is in remainder
int remainder[ maxc ][ maxk ]; //remainder储存余数,对每个模数有k个 // k possible remainder each
set<int> vis[ maxc ];
void init () {
total = 1;
bestc = 0;
for ( int i = 0; i < C; ++i ) {
// INPt
scanf ( "%d%d", &prime[ i ], &k[ i ] );
total *= k[ i ];
for ( int j = 0; j < k[ i ]; ++j )
scanf ( "%d", &remainder[ i ][ j ] );
sort ( remainder[ i ], remainder[ i ] + k[ i ] );
if ( k[ i ] * prime[ bestc ] < k[ bestc ] * prime[ i ] )
bestc = i;
}
}
void solveEnum ( int s ) {
for ( int i = 0; i < C; ++i ) {
if ( i == s )
continue;
vis[ i ].clear ();
for ( int j = 0; j < k[ i ]; ++j )
vis[ i ].insert ( remainder[ i ][ j ] );
}
for ( int t = 0; S; ++t ) {
for ( int i = 0; i < k[ s ]; ++i ) {
//枚举所有的n
// n % x == y
LL n = (LL)prime[ s ] * t + remainder[ s ][ i ];
if ( n == 0 )
continue;
bool ok = true;
for ( int c = 0; c < C; ++c ) {
if ( c == s )
continue;
//检验n是否也能对除s外的其他数成立
// vis[c]里面是否存在n%prime[c]
if ( !vis[ c ].count ( n % prime[ c ] ) ) {
ok = false;
break;
}
}
if ( ok ) {
printf ( "%lld\n", n );
if ( --S == 0 )
break;
}
}
}
}
// modify x & y, return gcd ( a,b )
LL ex_gcd ( LL a, LL b, LL &x, LL &y ) {
if ( b == 0 ) {
x = 1, y = 0;
return a;
}
int g = ex_gcd ( b, a % b, x, y );
int t = x;
x = y;
y = t - a / b * y;
return g;
}
int a[ maxc ];
vector<int> sol;
// https://brilliant.org/wiki/chinese-remainder-theorem/
// give solution to a set of x = ai % ni
LL chinese_remainder ( int len ) {
LL N = 1, sum = 0;
LL x, zi;
for ( int i = 0; i < len; ++i )
N *= prime[ i ];
for ( int i = 0; i < len; ++i ) {
int yi = N / prime[ i ];
ex_gcd ( prime[ i ], yi, x, zi );
// sum is a proper solution
// see proof in the link above
sum = ( sum + a[ i ] * zi * yi ) % N;
}
return sum;
}
//枚举所有可能的方程组
//再调用CRT
void dfs ( int dep ) {
if ( dep == C )
sol.push_back ( chinese_remainder ( C ) );
else {
for ( int i = 0; i < k[ dep ]; ++i ) {
a[ dep ] = remainder[ dep ][ i ];
dfs ( dep + 1 );
}
}
}
void solveChina () {
sol.clear ();
dfs ( 0 );
//按照余数排序
sort ( sol.begin (), sol.end () );
LL M = 1;
for ( int i = 0; i < C; ++i )
M *= prime[ i ];
for ( int i = 0; S; ++i )
for ( int j = 0; j < (int)sol.size (); ++j ) {
//恢复真正的解
LL n = M * i + sol[ j ];
if ( n > 0 ) {
printf ( "%lld\n", n );
if ( --S == 0 )
break;
}
}
}
int main () {
while ( scanf ( "%d%d", &C, &S ) == 2 && S + C ) {
init ();
if ( total > limit )
solveEnum ( bestc );
else
solveChina ();
printf ( "\n" );
}
return 0;
}