首先写一个DP。从小到大排列质数
p
1
=
2
,
p
2
=
3
,
…
p_1=2,p_2=3,\ldots
p1=2,p2=3,…。令
f
i
,
j
f_{i,j}
fi,j表示用
≥
p
j
\geq p_j
≥pj的质数凑出总和
i
i
i的方案数,
g
i
,
j
g_{i,j}
gi,j为对应的长度和。那么可以发现
1
0
18
10^{18}
1018对应的最大的总和
k
k
k在
2000
2000
2000左右,可以承受。
然后可以类似数位DP的思路来输出答案了:我们枚举总和,然后由于总和相同按字典序排列,因此可以从小往大搜索,记录当前前缀对应的数位和,如果中间有不需要输出的可以跳过。
时间复杂度大概是
O
(
k
2
log
k
+
r
−
l
)
\mathcal O(\frac{k^2}{\log k}+r-l)
O(logkk2+r−l),不太会具体分析。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int prime[505],dig[505],tot;
void pre() {
for(int i=2;tot<500;i++) {
bool ok=1;
for(int j=2;j*j<=i;j++)
if (i%j==0) {
ok=0;
break;
}
if (ok) {
prime[++tot]=i;
int x=i;
while (x) {
dig[tot]++;
x/=10;
}
dig[tot]+=2;
}
}
}
ll f[5005][505],g[5005][505];
void dp() {
for(int i=1;i<=tot+1;i++) f[0][i]=1;
int d=1;
ll s=0;
while (s<(ll)(1.5e18)) {
d++;
for(int i=tot;i>0;i--)
if (prime[i]<=d) {
f[d][i]=f[d][i+1]+f[d-prime[i]][i+1];
g[d][i]=g[d][i+1]+g[d-prime[i]][i+1]+f[d-prime[i]][i+1]*dig[i];
}
s+=f[d][1]*2LL+g[d][1];
}
}
char ans[110000];
int sz;
int st[505],cur[50],top;
ll L,R,len,lb;
void dfs(int sum,int d,int w) {
if (!sum) {
if (!sz) lb=len;
len+=w+2LL;
ans[++sz]='[';
for(int i=1;i<=top;i++) {
int x=st[i],cnt=0;
while (x) {
cur[++cnt]=x%10;
x/=10;
}
for(int i=cnt;i>0;i--) ans[++sz]='0'+cur[i];
if (i==top) ans[++sz]=']';
ans[++sz]=',';
ans[++sz]=' ';
}
return;
}
if (d>tot||prime[d]>sum) return;
if (f[sum][d]==f[sum][d+1]||len+(f[sum][d]-f[sum][d+1])*(w+2LL)+(g[sum][d]-g[sum][d+1])<L) {
len+=(f[sum][d]-f[sum][d+1])*(w+2LL)+(g[sum][d]-g[sum][d+1]);
dfs(sum,d+1,w);
return;
}
st[++top]=prime[d];
dfs(sum-prime[d],d+1,w+dig[d]);
top--;
if (len>=R) return;
dfs(sum,d+1,w);
}
void solve() {
for(int i=1;len<R;i++)
if (len+f[i][1]*2LL+g[i][1]<L) len+=f[i][1]*2LL+g[i][1];
else dfs(i,1,0);
}
int main() {
freopen("list.in","r",stdin);
freopen("list.out","w",stdout);
pre();
dp();
scanf("%lld%lld",&L,&R);
solve();
for(ll i=L;i<=R;i++) putchar(ans[i-lb]);
printf("\n");
return 0;
}