链接
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2854
题解
这个做法有些玄学
设
X
=
∏
x
i
X=\prod x_i
X=∏xi
我找
k
i
X
x
i
\frac{k_iX}{x_i}
xikiX最小的,然后枚举
a
n
s
=
k
x
i
+
y
j
ans=kx_i+y_j
ans=kxi+yj,(注意这里的
k
k
k和之前意义不同)
再依次检验这个解是不是也
x
x
x满足其它
c
−
1
c-1
c−1个条件,在
(
0
,
X
]
(0,X]
(0,X]内寻找解,如果解不够
S
S
S个,就加上
k
×
X
k\times X
k×X来构造剩余的解
复杂度是
O
(
C
X
k
i
x
i
)
O(C\frac{Xk_i}{x_i})
O(CxiXki)
算法复杂度分析
这个复杂度非常的玄学,
当
C
=
1
C=1
C=1时,这个算法显然是
O
(
S
)
O(S)
O(S)的
当
C
=
2
C=2
C=2时,极端数据就是
x
1
,
x
2
x_1,x_2
x1,x2都接近
2
16
2^{16}
216,
k
1
=
k
2
=
100
k_1=k_2=100
k1=k2=100,这个复杂度也不高
当
C
=
3
C=3
C=3时,极端数据就是
x
1
,
x
2
,
x
3
x_1,x_2,x_3
x1,x2,x3都接近
2
32
3
2^{\frac{32}{3}}
2332,
k
1
=
k
2
=
k
3
=
100
k_1=k_2=k_3=100
k1=k2=k3=100,代入上式会发现时间复杂度表达式的值高达
1
0
8
10^8
108数量级,但是为啥算法还是跑的很快?因为
k
k
k太大了,解分布的很密集,因此我还是很快就找到了前
S
S
S个解
所以我的实际计算次数其实和解分布的密集程度成负相关,那如果我让
k
k
k很小,是个什么情况?
比如这样的极端数据:
C
=
9
C=9
C=9,
x
i
x_i
xi是前
9
9
9个素数
2
,
3
,
5
,
7
,
11
,
13
,
17
,
19
,
23
2,3,5,7,11,13,17,19,23
2,3,5,7,11,13,17,19,23,
k
i
=
1
k_i=1
ki=1
那显然最小的解不会超过这9个素数的乘积
223092870
223092870
223092870,按照我的算法,我会枚举答案
N
=
y
1
,
23
+
y
1
,
46
+
y
1
.
.
.
.
N=y_1,23+y_1,46+y_1....
N=y1,23+y1,46+y1....,然后带入剩下的条件验证,实际计算次数不会超过
9699690
9699690
9699690
上述情形已经是极端情形,如果我让任何一个
k
i
k_i
ki增加,那么解的分布就会更密集,我就会更快找到答案,而答案变密集造成的影响远比
k
i
k_i
ki的值增加对复杂度的影响更大
思路提取
主要思路就是,如果一道题让你找满足 C C C个条件的某种解,那么可以先看一种条件,在这种条件下枚举解,在去验证其是否符合其它条件
代码
//数论
#include <bits/stdc++.h>
#define maxn 110
#define ll long long
#define linf (1ll<<60)
using namespace std;
set<ll> y[maxn];
ll C, S, x[maxn], X, lis[maxn];
ll read(ll x=0)
{
ll c, f=1;
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-48;
return f*x;
}
void init()
{
ll i, j, k;
*lis=0;
X=1;
for(i=1;i<=C;i++)
{
x[i]=read();
X*=x[i];
k=read();
y[i].clear();
for(j=1;j<=k;j++)y[i].insert(read());
}
}
bool check(ll N)
{
ll i;
for(i=1;i<=C;i++)if(y[i].find(N%x[i])==y[i].end())return false;
return true;
}
void work()
{
ll i, j, k, b, N, t=linf, pos, res=S+1;
set<ll>::iterator it;
for(i=1;i<=C;i++)if(X/x[i]*y[i].size()<t)t=X/x[i]*y[i].size(), pos=i;
for(k=0;k*x[pos]+x[pos]-1<=X and res;k++)
{
for(it=y[pos].begin();it!=y[pos].end() and res;it++)
{
N=k*x[pos]+*it;
if(check(N))
{
res--;
lis[++*lis]=N;
}
}
}
if(lis[1]==0)S++;
t=*lis;
while(*lis<=S)++*lis, lis[*lis]=lis[*lis-t]+X;
for(i=1;i<=S;i++)if(lis[i])printf("%lld\n",lis[i]);
putchar(10);
}
int main()
{
while(scanf("%lld%lld",&C,&S),C)init(), work();
return 0;
}