1. 程式人生 > >洛谷 P3312 [SDOI2014]數表 解題報告

洛谷 P3312 [SDOI2014]數表 解題報告

P3312 [SDOI2014]數表

題目描述

有一張\(N*M\)的數表,其第\(i\)行第\(j\)列(\(1\le i \le n\)\(1 \le j \le m\))的數值為能同時整除\(i\)\(j\)的所有自然數之和。給定\(a\),計算數表中不大於\(a\)的數之和。

輸入輸出格式

輸入格式:

輸入包含多組資料。

輸入的第一行一個整數\(Q\)表示測試點內的資料組數

接下來\(Q\)行,每行三個整數\(n\)\(m\)\(a\)(\(|a| \le 10^9\))描述一組資料。

輸出格式:

對每組資料,輸出一行一個整數,表示答案模\(2^{31}\)的值。

說明

\(1 \le N,M\le 10^5\)\(1 \le Q \le 2*10^4\)


按道理就是先不管條件。

然後化簡式子得到了

\[\sum_{k=1}^{\min(n,m)}k\lfloor\frac{n}{k}\rfloor\lfloor\frac{m}{k}\rfloor\]

想想確實不能拿掉一些東西,否則沒法做。

想到有\(\mathbf {Id}=\sigma*\mu\)

於是把式子拆開

\[\sum_{k=1}^{\min(n,m)}\lfloor\frac{n}{k}\rfloor\lfloor\frac{m}{k}\rfloor\sum_{d|k}\sigma(d)\mu(\frac{k}{d})\]

或者換個方向反演也可以得到這個式子。

我們知道格子\((i,j)\)的值就是\(\sigma(gcd(i,j))\)

於是我們可以離線讀入,然後從小到大把\(\sigma\)加入字首和。

具體的,可以拿一個樹狀陣列維護\(\sum_{d|k}\sigma(d)\mu(\frac{k}{d})\)的字首和,然後每次查詢或者加一些東西進去就可以了。

複雜度\(O(n\log^2n+Q\sqrt n\log n)\)


Code:

#include <cstdio>
#include <algorithm>
const int N=1e5;
std::pair <int,int> sigma[N+10];
int mu[N+10],v[N+10];
void init()
{
    for(int i=1;i<=N;i++) mu[i]=1,sigma[i]=std::make_pair(i+1,i);
    sigma[1].first=1;
    for(int i=2;i<=N;i++)
    {
        if(!v[i]) mu[i]=-1;
        for(int j=i*2;j<=N;j+=i)
        {
            sigma[j].first+=i;
            if(!v[i])
            {
                if((j/i)%i==0) mu[j]=0;
                else mu[j]*=-1;
                v[j]=1;
            }
        }
    }
    std::sort(sigma+1,sigma+1+N);
}
int min(int x,int y){return x<y?x:y;}
struct node
{
    int n,m,a,id;
    bool friend operator <(node n1,node n2){return n1.a<n2.a;}
}qry[N+10];
int s[N+10],ans[N+10],pos=1,T;
void add(int p,int d){while(p<=N)s[p]+=d,p+=p&-p;}
int ask(int p){int sum=0;while(p)sum+=s[p],p-=p&-p;return sum;}
void change(int d)
{
    while(sigma[pos].first<=d&&pos<=N)
    {
        for(int i=sigma[pos].second;i<=N;i+=sigma[pos].second)
            add(i,sigma[pos].first*mu[i/sigma[pos].second]);
        ++pos;
    }
}
int main()
{
    init();
    scanf("%d",&T);
    for(int i=1;i<=T;i++)
        scanf("%d%d%d",&qry[i].n,&qry[i].m,&qry[i].a),qry[i].id=i;
    std::sort(qry+1,qry+1+T);
    for(int i=1;i<=T;i++)
    {
        change(qry[i].a);
        int n=qry[i].n,m=qry[i].m,sum=0;
        for(int l=1,r;l<=min(n,m);l=r+1)
        {
            r=min(n/(n/l),m/(m/l));
            sum+=(n/l)*(m/l)*(ask(r)-ask(l-1));
        }
        ans[qry[i].id]=sum&0x7fffffff;
    }
    for(int i=1;i<=T;i++) printf("%d\n",ans[i]);
    return 0;
}

2018.11.26