1. 程式人生 > >[bzoj3529] [Sdoi2014]數表

[bzoj3529] [Sdoi2014]數表

Description

有一張 n×m 的數表,其第 i 行第 j 列(1 <= i <= n, 1 <= j <= m)的數值為

能同時整除 i 和 j 的所有自然數之和。給定 a , 計算數表中不大於 a 的數之和。

Input

輸入包含多組資料。

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

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

1 < =N.m < =10^5 , 1 < =Q < =2×10^4

Output

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

Sample Input

2
4 4 3
10 10 5

Sample Output

20
148

solution

首先忽略\(a\)這個條件,題目讓求的是:
\[ ans=\sum_{i=1}^n\sum_{j=1}^mf(gcd(i,j)) \]
其中,\(f(x)\)表示\(x\)的約數和。

然後,我們可以莫比烏斯反演一波,得到:
\[ ans=\sum_{T=1}^{min(n,m)}\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\sum_{d|T}f(d)\mu(\frac{T}{d}) \]
然後把後面那塊設為\(g\),即:
\[ g(n)=\sum_{d|n}f(d)\mu(\frac{n}{d}) \]


如果沒有a的限制,隨便搞搞這題就做完了。

然後很顯然可以發現,當\(f(d)\leqslant a\)時,\(f(d)\)才會對\(g(n)\)有貢獻。

考慮離線,對讀入按\(a\)排序,然後從小到大更新\(g\)

由於數論分塊的時候需要的是字首和,所以可以考慮拿個資料結構維護下,這裡樹狀陣列就是個很好的選擇。

然後其他的函式線篩或者大力算一下都行。

時間複雜度:\(O(n+n*log^2(n)+q*\sqrt{n}*log(n))\)

#include<bits/stdc++.h>
using namespace std;

#define int unsigned int 

void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}

void print(int x) {
    if(x<0) x=-x,putchar('-');
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

const int maxn = 1e5+1;

int f[maxn],mu[maxn],pri[maxn],vis[maxn],tot,p[maxn],s[maxn];

void sieve() {
    f[1]=mu[1]=1;
    for(int i=2;i<maxn;i++) {
        if(!vis[i]) pri[++tot]=i,mu[i]=-1,f[i]=i+1,p[i]=i,s[i]=i+1;
        for(int t,j=1;j<=tot&&i*pri[j]<maxn;j++) {
            vis[t=i*pri[j]]=1;
            if(i%pri[j]==0) {
                mu[t]=0;p[t]=p[i]*pri[j],s[t]=s[i]+p[t];
                f[t]=f[i]/s[i]*s[t];break;
            }
            s[t]=pri[j]+1,f[t]=f[i]*s[t],p[t]=pri[j],mu[t]=-mu[i];
        }
    }
    //for(int i=1;i<=10;i++) printf("%d %d %d %d\n",i,f[i],s[i],p[i]);;
}

struct Binary_Indexed_Tree {
    int tr[maxn];
    void add(int x,int v) {for(int i=x;i<maxn;i+=i&-i) tr[i]+=v;}
    int query(int x,int ans=0) {for(int i=x;i;i-=i&-i) ans+=tr[i];return ans;}
}BIT;

int solve(int n,int m) {
    int T=1,ans=0;
    while(T<=n) {
        int pre=T;T=min(n/(n/T),m/(m/T));
        ans+=(n/T)*(m/T)*(BIT.query(T)-BIT.query(pre-1));T++;
    }return ans;
}

int n;
struct input {
    int n,m,a,id;
    int operator < (const input &rhs ) const {return a<rhs.a;}
}in[maxn],ans[maxn];

struct Pair {
    int first,second;
    int operator < (const Pair &rhs ) const {return first<rhs.first;}
}g[maxn];

int cmp(input a,input b) {return a.id<b.id;}

signed main() {
    sieve();read(n);
    for(int i=1;i<=n;i++) read(in[i].n),read(in[i].m),read(in[i].a),in[i].id=i;
    sort(in+1,in+n+1);for(int i=1;i<maxn;i++) g[i].first=f[i],g[i].second=i;
    sort(g+1,g+maxn);int now=0;
    for(int i=1;i<=n;i++) {
        while(g[now+1].first<=in[i].a&&now+1<maxn) {
            now++;
            for(int i=g[now].second;i<maxn;i+=g[now].second)
                BIT.add(i,g[now].first*mu[i/g[now].second]);
        }
        if(in[i].n>in[i].m) swap(in[i].n,in[i].m);
        ans[i].a=solve(in[i].n,in[i].m),ans[i].id=in[i].id;
    }
    sort(ans+1,ans+n+1,cmp);
    for(int i=1;i<=n;i++) write(ans[i].a&((1u<<31)-1));
    return 0;
}