CC

自然万物都趋向从有序变得无序

0%

UVA 11754 Code Feat

题目链接:点我

题目


题意:

给你 C 个约束条件,你需要得出 S 个最小的解. 每一个约束条件可以表现为他的某一个ans ,满足 ans mod X = YiY_i 的形式,

思路:

中国剩余定理: (维基百科) 这里我们可以在选取了每个条件中的余数后用中国剩余定理解决, 那么问题是我们怎么样选出最小的 S 个. 因为我们在选取余数的时候用的是深搜,但是这样如果数据太大很容易超时,那么我们设定一个限制条件,当余数的数量大于某个值的时候,我们就用其他方法, 这个时候我们可以用 X / K 最小的一组开始枚举 它所能组成的所有的数,然后判断这个数,是否符合其他 的条件,如果符合就输出.
另一个情况就是余数个数比较少的情况,那么此时我们可以暴力枚举所有余数的组合情况,然后用中国剩余定理解出ans.

中国剩余定理:

这里写图片描述

对于上面的式子,我们要解出 x 的值,
令 M = m1m_1 * m2m_2 * m3m_3 *m4m_4mnm_n;
然后我们设MiM_i = M / mim_i ;
然后取 tit_iMiM_i 的关于 mim_i 逆元(这里求逆元我们用扩展欧几里得算法),
最后我们得出通解: x = $\sum_{i = 1} ^ n a_i * t_i * M_i $ + k * M, k $\in $ ZZ,

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<vector>
#include<set>
using namespace std;

typedef long long LL;
LL x[20];
int k[20];
int c, s;
int a[105];
vector<int > ans;
int num;
int y[20][105];

void exgcd(LL mi, LL xi, LL &e, LL &d){
if(!xi){
e = 1;
d = 0;
}else {
exgcd(xi, mi % xi, d, e);
d-= mi / xi * e;
}
}

LL china(){//中国剩余定理
LL m = 1;
LL cnm = 0;
LL e, d;
for(int i = 1; i <= c; ++i)
m *= x[i];
for(int i = 1; i <= c;++i){
LL mi = m / x[i];
exgcd(mi, x[i], e, d);//扩展欧几里得求逆元
cnm = (cnm + e * mi * a[i] ) % m;
}
return (cnm + m) % m;
}

void dfs(int cur){
if(cur == c + 1){
LL tmp = china();
ans.push_back(tmp);
return ;
}
for(int i = 1; i <= k[cur];++i){
a[cur] = y[cur][i];
dfs(cur+1);
}
}

void solvechina(){
LL m = 1;
ans.clear();
dfs(1);
sort(ans.begin(), ans.end());
for(int i = 1; i <= c;++i) m *= x[i];
for(int i = 0; s; ++i)
for(int j = 0; j < ans.size();++j){
LL ansk = m * i +ans[j];
if(ansk > 0){
printf("%lld\n",ansk);
--s;
if(s == 0)
break;
}
}
}

set<int > v[20];
void solvenum(int cur){
for(int i = 1; i <= c; ++i)
if(i != cur){
v[i].clear();
for(int j = 1; j <= k[i]; ++j)
v[i].insert(y[i][j]);
}for(int i = 0; s; ++i)
for(int j = 1; j <= k[cur]; ++j){
LL m = (LL)i * x[cur] +y[cur][j];//从小到大枚举ans,
if (!m) continue;
bool flag = true;
for(int l = 1; l <= c; ++l)
if(l !=cur && !v[l].count(m % x[l])){//判断ans是否符合所有的情况,
flag = false;
break;
}if(flag){
printf("%lld\n",m);
--s;
if(s == 0)
break;
}
}
}


int main(){
while(scanf("%d %d", &c, &s) != EOF &&(c||s)){
LL total = 1;
int limit = 1;
for(int i = 1; i <= c; ++i){
scanf("%lld %d", &x[i], &k[i]);
total *= k[i];
if(x[i]*k[limit] > x[limit] * k[i])//选取枚举的标准
limit = i;
for(int j = 1; j <= k[i]; ++j)
scanf("%d", &y[i][j]);
sort(y[i] + 1, y[i] + 1 +k[i]);
}
if(total > 10000)
solvenum(limit);
else solvechina();
printf("\n");
}
return 0;
}