比赛的时候就预感到这题能出,但是会耗时比较多。结果最后是出了,但是有更简单的题没出。
是不是错误的决策呢?谁知道呢
题目意思:
定义f(x) = x分解质因数出来的因子个数
如 x = p0 * p0 * p0 * p1 * p2,则f(x) = 5
特殊的, f(1) = 0
求 i = [1..n], j = [1..m] 组成的n*m组(i, j)对中,有多少组f( gcd(i,j) ) <= p
考虑简化版本,p = 0,即求有多少组 gcd(i,j) == 1。
见HDU 1695 http://acm.hdu.edu.cn/showproblem.php?pid=1695
师承叉姐(现在似乎叫御坂姐姐了...)的技能 莫比乌斯函数 + sqrt分块 可到0MS的题。
这题思路其实也大致差不多。
设d(x) 表示 gcd(i, j) 整除 x 的部分,容斥时的权值。
则满足 sigma( d(i) ) (i为x的所有约数) = ( f(x) >= p? 0 : 1 )
喜闻乐见,形如
for(i = 1;i<=n;i++)
for(j = i;j<=n;j+=i)
的nlogn预处理法
先预处理p=0..18时 每个数字在容斥中占的权值,然后求前缀和,最后sqrt分块计算。
Ps:由于题目的n,m范围下,f(x)最大为18,所以当p>18时,答案就为n*m
代码如下:
1 #include <vector> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <algorithm> 6 using namespace std; 7 typedef long long ll; 8 const int N = 500005; 9 int pr[N],p[N],cn[N],lp; 10 //预处理素数,和f(x),记为cn[x] 11 void gp(){ 12 for(int i=2;i<N;i++){ 13 if(!pr[i]){ 14 p[lp++]=pr[i]=i; 15 cn[i] = 1; 16 } 17 for(int j=0;j<lp && i*p[j]<N;j++){ 18 int num = i*p[j]; 19 pr[num] = p[j]; 20 cn[num] = cn[i]+1; 21 if(i%p[j] == 0) break; 22 } 23 } 24 } 25 //预处理p = 0..18时的d(x),记为tn[p][x] 26 int tn[19][N]; 27 void gtn(){ 28 for(int i=0;i<19;i++){ 29 tn[i][1] = 1; 30 for(int j=2;j<N;j++){ 31 if(cn[j] - i == 1){ 32 for(int k=j;k<N;k+=j) 33 tn[i][k]--; 34 } 35 else if(cn[j] > i){ 36 int tmp = -1 - (tn[i][j]); 37 tn[i][j] = tmp; 38 if(tmp){ 39 for(int k=j+j;k<N;k+=j) 40 tn[i][k] += tmp; 41 } 42 } 43 } 44 for(int j=2;j<N;j++) tn[i][j]+=tn[i][j-1]; 45 } 46 } 47 void adn(vector<int> &s,int x){ 48 s.push_back(0); 49 for(int i=1;i*i<=x;i++){ 50 s.push_back(i); 51 s.push_back(x/i); 52 } 53 } 54 ll n,m; 55 int k,*sm; 56 ll gao(){ 57 vector<int> num; 58 adn(num,n); 59 adn(num,m); 60 sort(num.begin(),num.end()); 61 num.erase(unique(num.begin(),num.end()),num.end()); 62 ll ans = 0; 63 int l = num.size(); 64 sm = tn[k]; 65 for(int i=1;i<l;i++){ 66 int d = num[i]; 67 ll tmp = sm[d] - sm[num[i-1]]; 68 ans += tmp*(ll)(n/d)*ll(m/d); 69 } 70 return ans; 71 } 72 int main(){ 73 //freopen("in.txt", "r", stdin); 74 gp(); 75 gtn(); 76 int T; 77 scanf("%d",&T); 78 while(T--){ 79 scanf("%I64d%I64d%d",&n,&m,&k); 80 if(k>18 || (1<<k)>=max(n,m)){ 81 printf("%I64d\n",n*m); 82 continue; 83 } 84 printf("%I64d\n",gao()); 85 } 86 return 0; 87 }