【GDOI2016模拟3.16】幂

Description:

这里写图片描述
【GDOI2016模拟3.16】幂_第1张图片

题解:

x=pqii x = ∏ p i q i d=gcd(qi) d = g c d ( q i )

d>1 d > 1 ,我们先不考虑它。

d=1x>n d = 1 且 x > n ,则 xy x y 一定不会与其他有重的,贡献为B。

d=1x<=n d = 1 且 x <= n ,设 L=lognx L = l o g x n ,显然对于L一样的贡献是一样。

由于 (xy)z=xyz ( x y ) z = x y z ,所以问题转换为 x[1..L]y[1..B] x ∈ [ 1.. L ] , y ∈ [ 1.. B ] ,xy有多少不同取值。

划分一下范围变成问 [1..LB] [ 1.. L ∗ B ] 有多少数满足有至少一组xy=它,这个可以想到用容斥原理做,大概要维护lcm和最小的选了的数。

但是L最大是 29 29 ,会超时。

注意 lcm(129) l c m ( 1 − 29 ) 的约数个数不多,也就是说可能的lcm个数不多,于是按DAG顺序dp,复杂度就降了下来。

还有一种想法是分块考虑。

枚举i,当前块是 ((i1)B,iB] ( ( i − 1 ) ∗ B , i ∗ B ]

显然只有 x[i..L] x ∈ [ i . . L ] 的才会对它们产生影响,若一数是 x[i..L] x ∈ [ i . . L ] 的倍数,则这个数可以。

当时复杂度好像还是 229 2 29 ,若 [i..L] [ i . . L ] 中有两数d1、d2,满足 d1|d2 d 1 | d 2 ,显然只用保留d1,最大的话还剩15个数。

Code:

#include
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

const int M = 100000;

int bz[M + 5], p[M + 5], u[M + 5];
int n, m, s[100];
ll ans, sum;

int gcd(ll x, ll y) {return !y ? x : gcd(y, x % y);}

void Shai() {
    fo(i, 2, M) {
        if(!bz[i]) p[++ p[0]] = i, u[i] = i;
        fo(j, 1, p[0]) {
            int k = i * p[j];
            if(k > M) break;
            bz[k] = 1; u[k] = p[j];
            if(i % p[j] == 0) break;
        }
    }
    u[1] = 1;
}

void dg(int x, int y, int z) {
    if(z == 1) return;
    ans --;
    fo(j, y, p[0]) {
        int k = 1;
        for(ll u = (ll) x * p[j]; u <= n; u *= p[j], k ++)
            dg(u, j + 1, gcd(k, z));
        if(k == 1) return;
    }
}

int t, w[M], bx[M];

void dfs(int x, ll lcm, int fu) {
    if(x > w[0]) {
        if(lcm == 1 && fu == -1) return;
        sum += fu * ((ll) t * m / lcm - (ll) (t - 1) * m / lcm);
        return; 
    }
    dfs(x + 1, lcm, fu);
    dfs(x + 1, lcm * w[x] / gcd(lcm, w[x]), -fu);
}

int main() {
    Shai();
    scanf("%d %d", &n, &m);
    ans = n;
    dg(1, 1, 0);
    ans = ans * m; ans ++;
    fo(i, 2, M) if(i * i <= n) {
        int x = i, gd = 0;
        while(x > 1) { 
            int y = u[x], z = 0;
            while(x % y == 0) x /= y, z ++;
            gd = gcd(gd, z);
        }
        x = 1; int y = 0;
        while((ll) x * i <= n) x *= i, y ++;
        if(gd == 1) {
            ans -= m;
            s[y] ++;
        }
    } else break;
    fo(i, 2, 29) if(s[i]) {
        sum = 0;
        fo(j, 1, i) {
            t = j; w[0] = 0;
            fo(k, j, i) bx[k] = 0;
            fo(k, j, i) if(!bx[k]) {
                w[++ w[0]] = k;
                fo(o, 1, i / k) bx[o * k] = 1;
            }
            dfs(1, 1, -1);
        }
        ans += sum * s[i];
    }
    printf("%lld\n", ans);
}

你可能感兴趣的:(动态规划,数论杂集)