bzoj 4815: [Cqoi2017]小Q的表格

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4818
思路:
看到这道题我就想到当年noip模拟赛T1 是一个伪装的”很好”的辗转相除而我做了半天的悲惨经历。。感动的落泪。
首先注意到那个规则就是辗转相除,不过不是标准形式,化成标准形式后多迭代几次很容易发现性质:
f(a,b)=abgcd(a,b)2f(gcd(a,b),gcd(a,b))
这个通过归纳法也很容易证明
这样ans = ki=1kj=1f(i,j)
化简这个式子,注意到一个经典问题:
ni=1[(i,n)=1]i=n(φ+e)2
利用这个我们的式子变成了:
ni=1f(i,i)g(ni)
其中 g(n)=ni=1iiϕ(i)
可以做到线性预处理 g
那么暴力维护修改的话就是套一个bit,明显T飞了。。。
到这里就非常像我bzoj 3529的做法了,一道披着数论外衣的数据结构狼?
注意到修改和查询是不均衡的,用序列分块就可以解决了,
这样就能做到 O(1) 查询, O(n) ,总的复杂度: O(mn)
另外按bzoj 3529那个题的做法我们也可以用定期重构的分块解决这个问题,这不过这里因为 n 很大的原因,重构次数不能太多,我估计在5次左右就可以了。。。应该也是可以通过的吧。
感觉自己基础差的要死。。。一开始连 φ 都筛错了半天没看出来,难怪滚粗呢QAQ

代码(分块):

#include
#include
#include
#include
#include
#define N 4000002
using namespace std;
typedef long long LL;
LL n,m,phi[N],g[N],now[N];
const LL P = 1000000007LL;
    inline void in(LL &x)
    {
        char c;
        while (!isdigit(c = getchar()));
        x = (c ^ 48);
        while (isdigit(c = getchar())) x = 10 * x + (c ^ 48);
    }
    inline LL gcd(LL a,LL b)
    {
        return (!b) ? (a) : gcd(b,a % b);
    }

    inline void inc(LL &x,LL y)
    {
        x += y;
        if (x >= P) x -= P;
    }

        int cnt,prime[N];
        bool not_prime[N];
        inline void Sieve()
        {
            memset(not_prime,0,sizeof(not_prime));
            cnt = 0; phi[1] = 1;
            for (int i = 2;i <= n; ++i)
            {
                if (!not_prime[i]) 
                {
                    prime[++cnt] = i;
                    phi[i] = i - 1;
                }
                for (int j = 1;j <= cnt; ++j)
                    if (prime[j] * i > n) break;
                    else
                    {
                        not_prime[prime[j] * i] = 1;
                        if (i % prime[j]) 
                            phi[i * prime[j]] = phi[i] * (prime[j] - 1);
                        else
                        {
                            phi[i * prime[j]] = phi[i] * prime[j];
                            break;
                        }
                    }
            }
            g[0] = 0;
            for (int i = 1;i <= n; ++i)
                g[i] = (g[i - 1] + 1LL * i * i % P * phi[i] % P) % P;

        }
            LL S[N];
            int num,belong[N],lb[N],rb[N],B;
        inline void Divide()
        {
            B = (int)(sqrt(n)) ;
            for (int i = 1;i <= n; ++i)
                belong[i] = (i - 1) / B + 1;
            belong[0] = belong[n + 1] = -1;
            for (int i = 1;i <= n; ++i)
            {
                if (belong[i] != belong[i - 1]) lb[belong[i]] = i;
                if (belong[i] != belong[i + 1]) rb[belong[i]] = i;
            }
            num = belong[n];
            for (int i = 1;i <= num; ++i)
                {
                    int le = lb[i],re = rb[i];
                    S[le] = now[le] % P;
                    for (int j = le + 1;j <= re; ++j)
                            S[j] = (S[j - 1] + now[j]) % P;
                }
        }

    inline void Pre()
    {
        for (int i = 1;i <= n; ++i) 
            now[i] = 1LL * i * i;
        Sieve();
        Divide();
    }

inline void init()
{
    in(m); in(n);
    Pre();  
}

        inline void change(LL a,LL b,LL x,LL k)
        {
            LL last,GCD = gcd(a,b),delta;
            last = now[GCD];
            now[GCD] = x / (a * b / (GCD * GCD));
            delta = (now[GCD] - last) % P;
            if (delta < 0) delta += P;
            int re = rb[belong[GCD]];
            for (int i = GCD;i <= re; ++i) 
                inc(S[i],delta);
        }

            LL ans;
                inline LL GET(LL l,LL r)
                {
                    int ll = belong[l],rr = belong[r];
                    LL Sum = 0;
                    if (ll == rr)
                    { 
                        if (l == lb[ll]) inc(Sum,S[r]);
                        else inc(Sum,(S[r] - S[l - 1] + P) % P);
                        return Sum;
                    }
                    Sum = S[r];
                    if (l == lb[ll]) inc(Sum,S[rb[ll]]);
                    else inc(Sum,(S[rb[ll]] - S[l - 1] + P) % P);
                    for (int i = ll + 1;i < rr; ++i)
                        inc(Sum,S[rb[i]]);
                    return Sum; 
                }

        inline void Query(LL a,LL b,LL x,LL k)
        {
            ans = 0;
            for (int i = 1,j;i <= k;i = j + 1)
            {
                j = k / (k / i);
                inc(ans,g[k / i] * GET(i,j) % P);
            }
            printf("%lld\n",ans);
        }

    LL a,b,x,k;
inline void DO_IT()
{
    for (int i = 1;i <= m; ++i)
    {
        in(a); in(b); in(x); in(k);
        change(a,b,x,k);
        Query(a,b,x,k);
    }
}

int main()
{
    init();
    DO_IT();
    return 0;
}

你可能感兴趣的:(bzoj 4815: [Cqoi2017]小Q的表格)