• 【GDOI2016模拟3.16】幂(容斥 + 模型复杂转化)


    【GDOI2016模拟3.16】幂
    • (Xin[1,A],Yin[1,B]),问:(x^y)的不用取值个数.

    • (A,B)都是(10^9)级别.

    • 然后我们开搞.

    • 首先,假设一个合法的(x)可以表示为(x=prod p_i^{q_i}),那么令(d=gcd(q_1,q_2...q_k))

    • 假设(d>1),显然我们不需要单独考虑,因为它可以继续化简,我们找到最简的那个数然后去一次性处理.

    • 那么此时所有情况都变成了(d=1).

    • 此时再分两种情况讨论,因为我们现在实际上把问题转化为了:

      (xin[1,A])(yin [1,B]),求((c^x)^y)的不同个数.

    • 然后这个问题显然当(x>sqrt{A})时贡献就是(B)

    • 所以题目转化为:

      (xin[1,sqrt{A}],yin[1,B])时,((c^x)^y)的不同个数.

    • 于是我们可以正难则反,计算一下不合法的个数,假设(k={log_x}^n),那么问题又可以转化为:

      (xin[1,k],yin[1,B])(xy)的相同个数.

    • 于是这个问题值得思考一下.

    • 咱有一个经典套路:分块计算;

    • 即把总范围([1,KB])变成(K)块,每一块都长的像这样的形式:

    [[(i-1)B+1,iB] | (1le i le K) ]

    • 那么现在单独考虑每一块的贡献.

    • 然后假设我们现在处理第(i)个块,那么如果其中某一个数可以被表示为(x*y)的形式.

    • 则必定存在一个(din [i,k]),满足(d|x*y).

    • 因为这样子就必定能满足(xin [1,K],yin [1,B])这两个条件.

    • 所以现在题目再次被转化:

      等价于我们要求在区间([(i-1)B+1,iB])范围内有多少个数是([i,k])区间中某一个数的倍数.

    • 这次转化以后我们发现问题就变得熟悉了...

    • 因为(B)比较大,(k)貌似小,所以,,,, 这TM容斥一下不就好了??

    • 但是,我们发现,(k)最大可能为(30),这意味着,(2^{30})是接受不了的...

    • 但是我们很容易想到,如果当乘上一个数的lcm没有变化之后,就不需要去弄.

    • 这样子的话时间一下子就降了下来,反正怎么打都能过~

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    
    #define ll long long
    #define F(i, a, b) for (ll i = a; i <= b; i ++)
    #define sqr(x) ((x) * (x))
    
    const ll N = 5e5;
    
    ll ans, sum;
    
    ll A, B, up, P[N];
    
    bool vis[N];
    
    using namespace std;
    
    void Init() {
    	#ifndef ONLINE_JUDGE
    		freopen("data.in", "r", stdin);
    	#endif
    	
    	scanf("%lld%lld", &A, &B);
    	
    	up = int(sqrt(A)) + 1;
    	
    	ans = 1LL * (A - 1) * B + 1;
    }
    
    void GetPrime() {
    	F(i, 2, N - 1) {
    		if (!vis[i])
    			P[++ P[0]] = i;
    		F(j, 1, P[0]) {
    			if (P[j] * i >= N) break;
    			vis[P[j] * i] = 1;
    			if (i % P[j] == 0) break;
    		}
    	}
    }
    
    ll gcd(ll x, ll y){
    	return y == 0 ? x : gcd(y, x % y);
    }
    
    void dfs(ll x, ll v, ll st, ll en, ll g, ll k) {
    	if (x > k) {
    		sum += 1LL * (en / v - st / v) * g;
    		return;
    	}
    	if (x != gcd(v, x)) {
    		dfs(x + 1, v * x / gcd(v, x), st, en, g * (- 1), k);
    		dfs(x + 1, v, st, en, g, k);
    	}
    }
    
    ll Solve(ll x) {
    	ll cnt = -1;
    	for (ll tmp = 1; tmp <= A; tmp *= x, cnt ++);
    
    	sum = 0;
    	F(i, 2, cnt)
    		dfs(i, 1, (i - 1) * B, i * B, 1, cnt);
    
    	return sum;
    }
    
    void Doit() {
    	F(i, 2, up) {
    		ll tmp = i, yes = 0;
    		F(j, 1, P[0]) {
    			if (sqr(P[j]) > i) break;
    			
    			ll cnt = 0;
    			while (tmp % P[j] == 0) tmp /= P[j], cnt ++;
    			
    			yes = gcd(yes, cnt);
    			if (yes == 1) break;
    		}
    		
    		if (tmp > 1 || yes == 1)
    			ans -= Solve(i);
    	}
    	
    	printf("%lld
    ", ans);
    }
    
    int main() {
    	Init();
    	
    	GetPrime();
    	
    	Doit();
    }
    
  • 相关阅读:
    Android SDK Android NDK 官方下载地址
    编码转换工具 源码
    st_mode的剖析
    关于 python 字符编码的一些认识
    MFC中的argc和argv参数
    VC实现文件拖拽获取文件名
    CString 转 int
    《C语言程序设计实践教程》实验题源程序
    C语言 文件操作 结构体与文件 fgetc fputc fread fwrite
    C++语言 创建状态栏
  • 原文地址:https://www.cnblogs.com/Pro-king/p/9383457.html
Copyright © 2020-2023  润新知