传送门:点击打开链接
题意:给定几组除数和余数,被除数两两互质,每个被除数对应的余数有多个,问满足题意的被除数,并从小到大排列
思路:中国剩余定理+暴力 中国剩余定理在解决余数较多问题时显得较为笨拙,于是,当余数达到某个值以上时,暴力求解更快一些
反思:这道题我T了好久,最后发现在暴力求解时,本可以用set存储每个除数所对应的余数,然后判断某个数模该除数的结果是否在set中,然而我却用dfs找......(现在想来真是智障),以前从没用过set,以后多用吧...红黑树还是快的啊
收获:在这道题上有一个难懂的地方,本题有signmaxi标记,这个标记对应的除数是用来在暴力时建立被除数的,选择标记的原则是m[i]/k[i]值最大(m[i]:除数,k[i]:除数对应的余数个数),这么选的原因是,暴力时建立被除数个数的多少取决于除数大和余数少的数对(以5和1e6举例, 5m+n(m从1开始循环,0<=n < 5)循环许多次也是小于1e6,这样轻率的选择标记徒增时间复杂度 | 余数越少,5m + n的n的选择性就越少,能满足该等式的整数就越少。仔细想想当n只有一种取值时1000000m+n在(0, LLONG_MAX)内还是很稀疏的)
代码如下:
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<set>
#include<vector>
using namespace std;
typedef long long ll;
vector<ll> ans;
set<ll> vis[15];
ll m[15], r[15][110], mm[15], k[15], all, tmp[15];
int c, s, signmaxi;
ll china()
{
ll sum = 0LL;
for(int i = 0; i < c; i++)
{
sum = (sum%all + (tmp[i]%all)*(mm[i]%all)%all)%all;
}
return sum > 0 ? sum : sum + all;
}
void chinadfs(int ci)
{
if(ci == c)
{
ll res = china();
ans.push_back(res);
return;
}
for(int i = 0; i < k[ci]; i++)
{
tmp[ci] = r[ci][i];
chinadfs(ci + 1);
}
}
void exgcd(ll &x, ll &y, ll a, ll b)
{
if(b == 0)
{
x = 1;
y = 0;
return;
}
exgcd(x, y, b, a % b);
ll tmp = x;
x = y;
y = tmp - a / b * y;
}
void chinasolve()
{
for(int i = 0; i < c; i++)
{
mm[i] = all / m[i];
}
ll x, y;
for(int i = 0; i < c; i++)
{
exgcd(x, y, mm[i], m[i]);
mm[i] *= x;
}
chinadfs(0);
sort(ans.begin(), ans.end());
int siz = ans.size();
int ss = s;
for(int i = 0; ss; i++)
{
for(int j = 0; j < siz; j++)
{
printf("%lld\n", ans[j] + i * all);
ss--;
if(ss == 0)
break;
}
}
}
void violent()
{
for(int i = 0; i < c; i++)
{
if(i == signmaxi)
continue;
for(int j = 0; j < k[i]; j++)
vis[i].insert(r[i][j]);
}
int ss = s;
bool ju = true;
for(int j = 0; ss; j++)
{
for(int i = 0; i < k[signmaxi]; i++)
{
ju = true;
ll tmp = r[signmaxi][i] + j * m[signmaxi];
if(tmp == 0)
continue;
for(int a = 0; a < c; a++)
{
if(a == signmaxi) continue;
if(!vis[a].count(tmp % m[a]))
{
ju = false;
break;
}
}
if(ju)
{
printf("%lld\n", tmp);
ss--;
if(ss == 0)
break;
}
}
}
}
int main()
{
while(~scanf("%d%d", &c, &s) && (c || s))
{
for(int i = 0; i < 15; i++)
vis[i].clear();
all = 1LL;
ans.clear();
signmaxi = 0;
ll total = 1LL;
for(int i = 0; i < c; i++)
{
scanf("%lld%lld", &m[i], &k[i]);
if(k[i] * m[signmaxi] < k[signmaxi] * m[i])
{
signmaxi = i;
}
all *= m[i];
total *= k[i];
for(int j = 0; j < k[i]; j++)
{
scanf("%lld", &r[i][j]);
}
sort(r[i], r[i] + k[i]);
}
if(total > 1000LL)
violent();
else
chinasolve();
printf("\n");
}
}
从Time limit exceed到0秒瞬过,还是有些小激动的...