[Acwing] 4319. 合适数对 hash+算术基本定理

前言

传送门 :

思路

a i ∗ a j = x k a_i*a_j=x^k aiaj=xk

根据算术基本定理 :
N = p 1 a 1 ∗ p 2 a 2 . . . p n a n N=p_1^{a1}*p_2^{a2}...p_n^{an} N=p1a1p2a2...pnan

可以将一个数拆为质因数的次幂的乘积

对于 x k x^k xk
我们将其拆分成
x = p 1 a 1 ∗ p 2 a 2 . . . p k a k x=p_1^{a1}*p_2^{a2}...p_k^{ak} x=p1a1p2a2...pkak

显然我们再把k次幂搞上去 , 对于每个 p p p的指数都变成了 k的倍数

(啊 这里因为我对 x k x^k xk进行质因数分解,看到 k k k的倍数的时候感觉不合乎常理,总算懂了)


因此开始分析

a j = p 1 a 1 ∗ p 2 a 2 . . . p k a k a_j=p_1^{a1}*p_2^{a2}...p_k^{ak} aj=p1a1p2a2...pkak
a i = p 1 b 1 ∗ p 2 b 2 . . . p k b k a_i=p_1^{b1}*p_2^{b2}...p_k^{bk} ai=p1b1p2b2...pkbk
根据上面推出来的
因此对于每一个 a i + b i % k = 0 ( a 这 里 是 指 数 a_i+b_i\%k=0(a这里是指数 ai+bi%k=0(a

数据范围 100000 100000 100000,所有数质因子的数量 n l n n ( 1000 − 10000 ) \frac{n}{ln^n} (1000-10000) lnnn(100010000)

但是对于每个数,出现过和其他数不同的质因子很少
2 ∗ 3 ∗ 5 ∗ 7 ∗ 11 = 2310 2*3*5*7*11=2310 235711=2310
2 ∗ 3 ∗ 5 ∗ 7 ∗ 11 ∗ 13 = 30030 2*3*5*7*11*13=30030 23571113=30030
因此最多只能包含六个质因子
每个数最多只有 6 6 6个质因子,需要补成 k k k的倍数

因此这里可以 h a s h hash hash一个匹配,但是这里可以使用他们的直接当作 h a s h hash hash
x = p 1 t 1 ∗ p 2 t 2 ∗ p 3 t 3 x =p_1^{t1}*p_2^{t2}*p_3^{t3} x=p1t1p2t2p3t3
y = q 1 r 1 ∗ q 2 r 2 ∗ q 3 r 3 y=q_1^{r1}*q_2^{r2}*q_3^{r3} y=q1r1q2r2q3r3
如果这两个中但凡出现一个不同那么 x y xy xy都不相同

code

const int N = 100010;

int n, m;
int cnt[N];

int power(int a, int b)
{
    LL res = 1;
    while (b -- )
    {
        res *= a;
        if (res >= N) res = 0;
    }
    return res;
}

int main()
{
    scanf("%d%d", &n, &m);

    LL res = 0;
    while (n -- )
    {
        int x;
        scanf("%d", &x);
        LL y = 1, z = 1;
        
        for (int i = 2; i * i <= x; i ++ )
            if (x % i == 0)
            {
                int s = 0;
                while (x % i == 0) x /= i, s ++ ;
                s %= m;
                if (s) 
                {
                    y *= power(i, s);
                    z *= power(i, m - s);
                }
            }
            
        /**
        这一整个过程都是hash
        **/

        if (x > 1) y *= x, z *= power(x, m - 1);
        if (z >= N) z = 0;

        res += cnt[z];
        cnt[y] ++ ;
    }

    printf("%lld\n", res);
    return 0;
}

Stl暴力 4483ms

const int N  = 1e5+10;
int a[N];
map<vector<pii>,int> cnt;

void solve()
{
	int n,m;cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	
	ll ans = 0 ;
	for(int i=1;i<=n;i++){//枚举每一个 a[i]
		int x = a[i];
		vector<pii> b,c;
		
		for(int j=2;j<=x/j;j++){//筛选质因数 O sqrt(n)
			int t = 0 ;
			
			while(x%j == 0) x/=j,++t;
			//j^t
		
			
			if(t%m) b.pb({j,t%m});
		}
		
		if(x>1){//分解质因子
			b.pb({x,1});
		}
		
		c = b;
		for(auto &[x,y] : c) y = m-y; //
		
		ans+= 1ll*cnt[c];
		cnt[b]++;
		
	}
	
	cout<<ans<<endl;
	
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值