zoukankan      html  css  js  c++  java
  • 【BZOJ3529】数表

    数表

    Description

      有一张 n*m 的数表,其第i行第j列(1<=i<=n,1<=j<=m)的数值为能同时整除 i和j的所有自然数之和。给定a,计算数表中不大于a的数之和。

    Input

      输入包含多组数据。
      输入的第一行一个整数Q,表示测试点内的数据组数;
      接下来Q行,每行三个整数n,m,a(|a|<=10^9 )描述一组数据。

    Output

      对每组数据,输出一行一个整数,表示答案模2^31的值。

    Sample Input

    2
    4 4 3
    10 10 5

    Sample Output

    20
    148

    Hint

    img

    不妨设(n<m)

    同时整除(i,j)的自然数之和就是(gcd(i,j))的约数之和。我们设(f(i)=sum_{d|i}d)
    则:

    [displaystyle ans=sum_{g=1}^{n}f(g)sum_{i=1}^{lfloor frac{n}{g} floor}sum_{j=1}^{lfloor frac{m}{g} floor}[gcd(i,j)=1]\ =sum_{g=1}^{n}f(g)sum_{i=1}^{lfloor frac{n}{g} floor}sum_{j=1}^{lfloor frac{m}{g} floor}sum_{d|i,d|j}mu(d) ]

    又来套路一波:设(T=gd)(displaystyle ans=sum_{T=1}^{n}sum_{d|T}mu(d)f(frac{n}{d})lfloor frac{n}{T} floorlfloor frac{m}{T} floor)

    然后又了a的限制后,我们就将询问和(f)都离线下来排序,加入树状数组里面。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<complex>
    #include<algorithm>
    #include<set>
    #include<map>
    #include<vector>
    #include<ctime>
    #include<queue>
    #include<iomanip>
    #define ll long long
    #define N 100005
    #define int ll
    
    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    int Q;
    int pri[N];
    bool vis[N];
    ll sum[N];
    int mu[N];
    struct node {
    	int id;
    	ll sum;
    	bool operator <(const node &a)const {
    		return sum<a.sum;
    	}
    }st[N];
    int cnt;
    struct query {
    	int n,m,id;
    	ll a;
    	bool operator <(const query &x)const {return a<x.a;}
    }q[20005];
    
    void pre(int n) {
    	mu[1]=1;
    	for(int i=2;i<=n;i++) {
    		if(!vis[i]) {
    			pri[++pri[0]]=i;
    			mu[i]=-1;
    		}
    		for(int j=1;j<=pri[0]&&i*pri[j]<=n;j++) {
    			vis[i*pri[j]]=1;
    			if(i%pri[j]==0) {
    				mu[i*pri[j]]=0;
    				break;
    			}
    			mu[i*pri[j]]=-mu[i];
    		}
    	}
    	for(int i=1;i<=n;i++) {
    		for(int j=i;j<=n;j+=i) {
    			sum[j]+=i;
    		}
    	}
    	cnt=n;
    	for(int i=1;i<=n;i++) st[i]=(node) {i,sum[i]};
    }
    
    ll tem[N];
    int low(int i) {return i&(-i);}
    const ll mod=(1ll<<31);
    void add(int v,ll f) {for(int i=v;i<=100000;i+=low(i)) (tem[i]+=f)%=mod;}
    void update(int v) {
    	for(int i=v;i<=100000;i+=v) {
    		if(!mu[i/v]) continue ;
    		add(i,(sum[v]*mu[i/v]%mod+mod)%mod);
    	}
    }
    
    ll Ask(int v) {
    	ll ans=0;
    	for(int i=v;i;i-=low(i)) (ans+=tem[i])%=mod;
    	return ans;
    }
    ll Ask(int l,int r) {return (Ask(r)-Ask(l-1)+mod)%mod;}
    ll ans[20005];
    int now;
    
    ll solve(int n,int m) {
    	if(n>m) swap(n,m);
    	int last;
    	ll ans=0;
    	for(int i=1;i<=n;i=last+1) {
    		last=min(n/(n/i),m/(m/i));
    		(ans+=1ll*(n/i)*(m/i)%mod*Ask(i,last)%mod)%=mod;
    	}
    	return ans;
    }
    
    signed main() {
    	pre(100000);
    	sort(st+1,st+1+cnt);
    	Q=Get();
    	for(int i=1;i<=Q;i++) {
    		q[i].n=Get(),q[i].m=Get(),q[i].a=Get();
    		q[i].id=i;
    	}
    	sort(q+1,q+1+Q);
    	int tag=1;
    	for(int i=1;i<=Q;i++) {
    		while(tag<=cnt&&st[tag].sum<=q[i].a) {
    			update(st[tag].id);
    			tag++;
    		}
    		now=i;
    		ans[q[i].id]=solve(q[i].n,q[i].m);
    	}
    	for(int i=1;i<=Q;i++) cout<<ans[i]<<"
    ";
    	return 0;
    }
    
  • 相关阅读:
    错误日志记录代码
    将数组转换成datatable
    C#类头注释
    判断当前页面是否接收到了Get或者Post请求
    HttpRequestUtil类
    WeChatUtil类
    返回上一页
    更改同步异步
    限制只能输入数字
    判断浏览器及版本
  • 原文地址:https://www.cnblogs.com/hchhch233/p/9996438.html
Copyright © 2011-2022 走看看