P8036 [COCI2015-2016#7] Prosti 题解

原题链接

博客园食用更佳

题目分析

我太菜了,只会打表,但是打表也要优雅!(正解似乎是二分)

一看数据范围如此之小,才 \(150\) 打表肯定可以鸭!

把考虑两种情况:

  1. 连续区间中有小于等于 \(m\) 的数。
  2. 没有小于 \(m\) 的,即全是大于 \(m\) 的质数。

前者可以在每个询问中得到,或者为了方便,所有的起点小于 \(150\) 的答案都在每个询问中单独完成。

code:

int sol(int k,int l,int m){
	int cnt=0;
	for(int i=1;i<=k;++i)
		if(i<=m || !vi[i])	++cnt;
	if(cnt==l)	return 1;
	for(int i=1;i<150;++i){
		if(i<=m || !vi[i])	--cnt;
		if(i+k<=m || !vi[i+k])	++cnt;
		if(cnt==l)	return i+1;
	}
	return -1;
}

对于情况 \(2\) 考虑先处理出 \(10^7\) 以内所有质数,然后枚举 \(10^7\) 内所有长度小于等于 \(150\) 的区间,并且统计其中的高兴数的个数。

这里为了方便用,令 st[i][j] 表示连续 \(i\) 个数中有 \(j\) 个高兴数的起点。

code:

memset(st,-1,sizeof st);
for(int i=1;i<=150;++i){
    int cnt=0;
    for(int j=151;j<=150+i;++j)
        if(!vi[j])	++cnt;
    st[i][cnt]=151;

    for(int j=151;j<=5e5;++j){
        if(!vi[j])	--cnt;
        if(!vi[j+i])	++cnt;
        if(st[i][cnt]==-1)	st[i][cnt]=j+1;
    }
}

再输出,就得到了一张表,这样答案就可以很快得到了。但是直接复制会有点大,代码也不美观。

仔细观察可以发现,表中的答案只有 st[138~150][1] 的结果是大于 \(5\times 10^5\) 的,而 \(0.5s\) 的时限也只支持枚举到 \(5\times10^5\)

所以我们在程序中只需要枚举到 \(5\times10^5\) 即可,再直接把上面得到的值赋给对应的就好了。

main 函数 code:

int main(){
	
	pri();//线性筛
	
	memset(st,-1,sizeof st);
	for(int i=1;i<=150;++i){
		int cnt=0;
		for(int j=151;j<=150+i;++j)
			if(!vi[j])	++cnt;
		st[i][cnt]=151;
		
		for(int j=151;j<=5e5;++j){
			if(!vi[j])	--cnt;
			if(!vi[j+i])	++cnt;
			if(st[i][cnt]==-1)	st[i][cnt]=j+1;
		}
	}
	st[138][1]=1357194;
	st[139][1]=1357194;
	for(int i=140;i<=150;++i)	st[i][1]=1561892;//打表得到的值
	
	int q=read();
	while(q--){
		int k=read(),l=read(),m=read();
		int tmp=sol(k,l,m);//上面给了代码的
		if(tmp!=-1)	printf("%d\n",tmp);
		else	printf("%d\n",st[k][l]);
	}
	
	return 0;
}

至此,打表的方法完美通过。

posted @ 2022-07-26 11:26  _yolanda  阅读(61)  评论(0编辑  收藏  举报