BZOJ 3529: [Sdoi2014]数表

二次联通门 : BZOJ 3529: [Sdoi2014]数表

 

 

Latex的公式写起来略麻烦。。。

 

设$\LARGE F(x)=\sum_{d|x} \lfloor \dfrac xd \rfloor$

$\LARGE Answer = \sum_{i=1}^n\sum_{j=1}^{m}F(\gcd(i,j))*[F(\gcd(i,j)<=a]$

 

当没有$a$的限制时

$\LARGE Answer = \sum_{d=1}^{n}\sum_{i=1}^{n}\sum_{j=1}^{m}F(d)*[\gcd(i,j)=d]$

              $\LARGE=\sum_{d=1}^{n}F(d)\sum_{i=1}^{n/d}\sum_{j=1}^{m/d}[\gcd(i,j)=1]$

 

由莫比乌斯反演得(见BZOJ 1101: [POI2007]Zap):

$\LARGE Answer = \sum_{d=1}^{n}F(d)\sum_{i=1}^n\mu(i)\lfloor \dfrac {n}{id} \rfloor \lfloor \dfrac {m}{id} \rfloor $

            $\LARGE =\sum_{d=1}^{n}\lfloor \dfrac nd \rfloor \lfloor \dfrac md \rfloor\sum_{i|d}\mu(\dfrac ni)F(i)$

然后对于 $\LARGE \sum_{d=1}^{n}\lfloor \dfrac nd \rfloor \lfloor \dfrac md \rfloor$可以$O(n\sqrt n)$计算

后一部分也可以预先预处理出来

 

再把$a$的限制加上

离线处理这个问题 

将$F$和$a$都排个序,那么要计算的就是$F(i)<=a$的$i$

那么就可以用树状数组维护$\LARGE \sum_{i|d}\mu(\dfrac ni)F(i)$的前缀和,每次插入小于当前$a$的$F[i]$,后每次询问前缀和即可

 

/*
    BZOJ 3529: [Sdoi2014]数表
    
    树状数组+莫比乌斯反演
    
*/
#include <cstdio>
#include <iostream>
#include <algorithm>

#define rg register

inline void read (int &n)
{
    rg char c = getchar ();
    for (n = 0; !isdigit (c); c = getchar ());
    for (; isdigit (c); n = n * 10 + c - '0', c = getchar ());
}
#define Max 100000

int t[Max + 9], p[Max + 9], mu[Max + 9]; bool is[Max + 9];
inline int min (int a, int b) { return a < b ? a : b; }

void Euler (int N)
{
    rg int i, j, k; int C = 0; mu[1] = 1;
    for (i = 2; i <= N; ++ i)
    {
        if (!is[i]) p[++ C] = i, mu[i] = -1;
        for (j = 1; j <= C; ++ j)
        {
            if ((k = i * p[j]) > N) break;
            is[k] = true;
            if (i % p[j] == 0) { mu[k] = 0; break; }
            else mu[k] = -mu[i];
        }
    }
}

struct Q 
{ 
    int x, y; Q (int a = 0, int b = 0) : x (a), y (b) { } 
    bool operator < (const Q &rhs) const
    { return x == rhs.x ? y < rhs.y : x < rhs.x; }
} f[Max + 9];

void F ()
{
    rg int i, j;
    for (i = 1; i <= Max; ++ i)
        for (j = i; j <= Max; j += i) f[j].x += i;
    for (i = 1; i <= Max; ++ i) f[i].y = i;
    std :: sort (f + 1, f + 1 + Max);
}

struct D 
{ 
    int n, m, a, id; 
    bool operator < (const D &rhs) const
    { return a < rhs.a; }
} q[Max + 9];
int Answer[Max + 9];

inline int Query (int x)
{
    int res = 0;
    for (rg int i = x; i; i -= i & (-i))
        res += t[i];
    return res;
}
void Change (int x, int k)
{ for (rg int i = x; i <= Max; i += i & (-i)) t[i] += k; }

int Ask (int N, int M)
{
    int res = 0, j; 
    if (N > M) std :: swap (N, M);

    for (rg int i = 1; i <= N; i = j + 1)
    {
        j = min (N / (N / i), M / (M / i));
        res += (N / i) * (M / i) * (Query (j) - Query (i - 1));
    }
    return res & 0x7fffffff;
}

int main (int argc, char *argv[])
{
    int N, M; rg int i, j, k;
    Euler (Max); F ();

    int T; read (T);
    for (i = 1; i <= T; ++ i)
        read (q[i].n), read (q[i].m), read (q[i].a), q[i].id = i;
    std :: sort (q + 1, q + 1 + T);

    for (i = 1, j = 1; i <= T; ++ i)
    {
        for (; j <= Max && f[j].x <= q[i].a; ++ j)
            for (k = f[j].y; k <= Max; k += f[j].y)
                Change (k, f[j].x * mu[k / f[j].y]);
        Answer[q[i].id] = Ask (q[i].n, q[i].m);    
    }

    for (i = 1; i <= T; ++ i)
        printf ("%d\n", Answer[i]);
    return 0;
}

 

posted @ 2017-12-19 20:05  ZlycerQan  阅读(162)  评论(0编辑  收藏  举报