2018ACM/ICPC南京站网络赛J Sum

题目链接

大意是定义f(x)为满足x==a*b且a和b均不能被除1以外的平方数整除的数对(a,b)的个数,给定n,输出x从1到n的f(x)的和。

比赛中这道题从看题,到写完,再到写个暴力对拍小数据也只花了二十五分钟而已,然后一发入魂。。。(虽然这题后就陷入了漫长的卡题)

我的解题思路的产生:首先看到数据范围2e7,虽然查询很少,但一般这个范围会让我联想到线性筛;其次,大致看下样例就能知道素数x的f(x)=2,而f(x)为0的都是x至少含有一个素因子大于等于3次的。

在线性筛的过程中,如果x是素数,对应的f(x)值赋值为2;
在筛素数倍数(即x=i*prime[j])的时候,如果i不是prime[j]的倍数,那么有f(x)=2*f(i),因为多出来的素因子prime[j]可以放a也可以放b,所以是在原来的基础上乘2;
如果i是 prime[j]^2 的倍数,则x=i*prime[j]至少含有因子 prime[j]^3,那么不论这个因子怎么分配,均会使得a,b其中一个数含有因子prime[j]^2,因此此时有f(x)=0;
如果i是 prime[j]的倍数,则x=i*prime[j]恰好是prime[j]^2的倍数,那么只能将prime[j]分别放在a和b中,和不放的数目是一样的,因此f(x)=f(i)。

筛完求个前缀和,O(1)查询即可。

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define For(i,a,b) for(int i=a;i<=b;i++)
const int N = 2e7+5;
const int mod=1e9+7;
bool notprime[N];
int cnt,prime[1300000];
int ans[N];
void init(){
	notprime[1]=1;ans[1]=1;
	For(i,2,N-1){
		if(!notprime[i]){
			prime[cnt++]=i;ans[i]=2;
		}
		for(int j=0;j<cnt&&i*prime[j]<N;j++){
			notprime[i*prime[j]]=1;
			if(i%prime[j]==0){
				if(i/prime[j]%prime[j]==0)
				ans[i*prime[j]]=0;
				else
				ans[i*prime[j]]=ans[i/prime[j]];
				break;
			}
			ans[i*prime[j]]=2*ans[i];
		}
	}
	For(i,1,N-1) ans[i]+=ans[i-1];
}
int t,n;
int main(){
	init();
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		printf("%d\n",ans[n]);
	}
	return 0;
}

时间:比赛中396ms,赛后443ms。

猜你喜欢

转载自blog.csdn.net/qq_38515845/article/details/82292071