1. 程式人生 > >2018ACM/ICPC南京站網路賽J Sum

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。