题解—玄学题
只能说我太菜了。
我考场上打的暴力,但是由于太菜没想到只有完全平方数的约数是奇数(这个大家都想到了)。
所以\(nm\ ln(mn)\)求了因子个数,然后暴力的。
最后由于比较贪开大了\(N\),导致被很慢的评测机把分卡没了。
实际上,还有一种方法可以优化连完全平方数都想不出来的菜鸡方法—\(O(n)\)求约数个数。
这个详见我的总结—线性筛及其扩展
我们其实就是求\(\sum d(i*j)\)是多少,但是这个东西并不需要\(O(m)\)枚举。
因为\(i*j\)只有是完全平方数\(d(i*j)\)才是奇数,只有是奇数才对答案有贡献。
所以我们就是想计算,对于给定的一个\(i\),\(i*j(j::1\sim m)\)中有多少是完全平方数。
我们只需要把\(i\)都找到一个数组\(gan[i]\)。
表示\(i\)的完全平方因子都被干完之后剩下的数。
之后直接\(\sqrt{(m/gan[i])}\)就是答案。
很显然的,因为只有\(i\)的非完全平方因子和\(j\)的非完全平方因子相同的时候他们的积才是完全平方数。
然后就是求1到\(m\)中有多少个数除\(gan[i]\)是完全平方数。
其中只有\(m/gan[i]\)个数是合法的(剩下的是小数)
并且他们还是从1开始连续的。
所以直接取根号就行。
接下来就是如何求\(gan[i]\)
做法一:暴力
我们可以先把所有的完全平方因子处理出来,然后把所有有完全平方因子的数打一个标记。
对于一个没有完全平方因子的数,我们枚举所有完全平方数,然后把他们的积乘上这个数。
和我考场上打的暴力思路很像(暴力算因子个数)。
复杂度是可以接受的,因为是\(N/4+N/9+N/16+...\)所以不会炸。
做法二:线性筛
可以发现,函数\(Gan(i)\)是一个积性函数,所以可以线性筛线性求。
还是按照套路处理就行了。
记录一个最小质因子出现的次数是奇数还是偶数。
- 如果这个数是质数,那么把出现次数设置成奇数,值设置成2。
- 如果一个数被筛到,由于他肯定是被最小质因子筛到的,所以我们讨论一下。
- 如果\(i%pr[j]==0\)我们把他出现的次数异或一下\(1\),之前是\(1\)就除掉,之前是\(0\)就乘上。
- 否则直接继承\(i\)的\(gan\)在乘上\(pr[j]\),出现次数设置成\(1\)就行了(因为\(i\)里面没有\(pr[j]\),说明是第一次出现)。
线性筛code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define R register int
#define int long
#define printf Ruusupuu = printf
#define scanf Ruusupuu = scanf
int Ruusupuu ;
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef pair<int , int> PI ;
const int N = 1e7 + 10 ;
inline void of(){ freopen( "c.in" , "r" , stdin ) , freopen( "c.out" , "w" , stdout ) ; }
inline void cf(){ fclose( stdin ) , fclose( stdout ) ; }
inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch - '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
int n , mx [N] , gan [N] , pr [N] , top ;
bool fg [N] ; L m ;
void sc(){
n = read() ; scanf( "%lld" , &m ) ;
}
void work(){
for( R i = 2 ; i <= n ; i ++ ){
if( !fg [i] ) pr [++ top] = i , gan [i] = i , mx [i] = 1 ;
for( R j = 1 ; j <= top && i * pr [j] <= n ; j ++ ){
fg [i * pr [j]] = 1 ;
if( i % pr [j] == 0 ){
if( mx [i] ){
mx [i * pr [j]] = 0 ;
gan [i * pr [j]] = gan [i] / pr [j] ;
}
else{
mx [i * pr [j]] = 1 ;
gan [i * pr [j]] = gan [i] * pr [j] ;
}
break ;
} gan [i * pr [j]] = gan [i] * pr [j] , mx [i * pr [j]] = 1 ;
}
}
gan [1] = 1 ; L ans = 0 ;
for( R i = 1 ; i <= n ; i ++ ){
L x = m / gan [i] , t = sqrt( x ) ;
ans += ( t % 2 ) ? -1 : 1 ;
} printf( "%lld\n" , ans ) ;
}
signed main(){
//of() ;
sc() ;
work() ;
//cf() ;
return 0 ;
}
刷表算code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define R register int
#define int long long
#define printf Ruusupuu = printf
#define scanf Ruusupuu = scanf
int Ruusupuu ;
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef pair<int , int> PI ;
const int N = 1e7 + 10 ;
inline void of(){ freopen( "c.in" , "r" , stdin ) , freopen( "c.out" , "w" , stdout ) ; }
inline void cf(){ fclose( stdin ) , fclose( stdout ) ; }
inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch - '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
int n , m , gan [N] , pp [N] , top ;
bool fg [N] ;
void sc(){
n = read() , m = read() ;
}
void work(){
for( R i = 1 ; i < N ; i ++ ){
int x = sqrt( i ) ;
gan [i] = 1 ;
if( x * x == i ) pp [++ top] = i , fg [i] = 1 ;
} gan [1] = 1 ;
for( R i = 2 ; i <= top ; i ++ )
for( R j = 1 ; pp [i] * j < N ; j ++ )
fg [j * pp [i]] = 1 ;
for( R i = 1 ; i <= top ; i ++ )
for( R j = 1 ; pp [i] * j < N ; j ++ ){
if( fg [j] ) continue ;
gan [j * pp [i]] *= j ;
}
int ans = 0 ;
for( R i = 1 ; i <= n ; i ++ ){
int x = m / gan [i] , t = sqrt( x ) ;
( t % 2 == 0 ) ? ans ++ : ans -- ;
}
printf( "%lld\n" , ans ) ;
}
signed main(){
//of() ;
sc() ;
work() ;
//cf() ;
return 0 ;
}

浙公网安备 33010602011771号