KUPC 2011 E: Fox Number

問題概要

正整数Nで、N = Π p[i]^e[i], p[i]:素数, p[i] < p[i+1], e[i] >= e[i+1]を満たすようなものをFox Numberという。A(<10^12)、B(<5*10^5)が与えられるとき、[A-B, A+B]に存在するFox Numberの個数を求める問題。

考えたこと

  • とりあえず、A-Bが負になったり1がFox Numberでないとかには気をつけとこう。
  • 一目、区間篩っぽいのが見えるけど。
  • とりあえず因数分解した結果を知っていれば判定はlog xくらいでできる。
  • Pollard rhoで素因数分解したら期待計算量O(N^1/4 * polylog(N))くらいなので一応L(区間の長さ、<10^6)* 10^3 * polylogくらいでぎりぎり間に合いそうとも言えるしpolylogで死にそうな気もする。今回は見送ろう。
  • mapで素因数分解した結果を持っておいて、小手先の高速化しても全然最大ケースが終わらない。
  • コンテスト終了後テスターのいもす先生に聞いたら、普通にやれば通る、ということだった。mapの重さとか枝刈りのセンスの無さが問題だったらしい。
  • 終了後、区間篩っぽい方法で通した。これは通しておくべきだったなあ。
  • Fox Numberの性質は枝刈り(Fox Numberでないことの判定)を容易にするための、しえるさんの優しさだと思っておく。

終了後通したコード

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long int64;

int64 A, B;
const int INF = 1<<29;
const int NOT_FOX = -1;

const int MAX_SIZE = 2000000;
bool isPrime[MAX_SIZE];
int div_num_instance[1200000];
int *div_num;

void seive(){
	memset(isPrime, -1, sizeof(isPrime));
	bool *ps = isPrime;
	ps[0] = ps[1] = false;
	for(int i=2; i*i<MAX_SIZE; i++)if(ps[i]){
		for(int j=i<<1; j<MAX_SIZE; j+=i){
			ps[j] = false;
		}
	}
}

//区間篩, 割り切る最小の素数も覚えておく
//[from, to)
int solve(int64 from, int64 to){

	fill(div_num + from, div_num + to, INF);

	for(int i=2; i<MAX_SIZE; i++)if(isPrime[i]){
		for(int64 j=max(2LL*i, (from+i-1)/i*i); j < to; j += i)if(div_num[j] != NOT_FOX){
			int64 t = j;
			int div_count = 0;
			while(t % i == 0){
				t /= i;
				div_count++;
			}
			if(div_count > div_num[j]){
				div_num[j] = NOT_FOX;
			}
			else{
				div_num[j] = div_count;
			}
		}
	}

	return (int)( to - from - count(div_num + from, div_num + to, NOT_FOX));
}


int main(){

	scanf("%lld%lld",&A, &B);

	div_num = div_num_instance - (A-B);

	//素因数分解
	seive();

	//本体
	printf("%d\n", solve(max(2LL, A-B), A+B+1));

	return 0;
}