Codeforces gym 101190 L

首先写一个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+rl),不太会具体分析。

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值