zoukankan      html  css  js  c++  java
  • [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}^nsum_{j=1}^mf(gcd(i,j)) ]

    其中,(f(x))表示(x)的约数和。

    然后,我们可以莫比乌斯反演一波,得到:

    [ans=sum_{T=1}^{min(n,m)}lfloorfrac{n}{T} floorlfloorfrac{m}{T} floorsum_{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('
    ');}
    
    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
    ",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;
    }
    
  • 相关阅读:
    dedecms织梦修改标题默认长度
    Python正课68 —— 实现面向对象编程
    Python正课67 —— 面向对象介绍
    Win10系统下提示VMware与Device/Credential Guard不兼容的解决方法
    周考5 月考1 内容
    Python正课66 —— ATM + 购物车 小有成就
    Python正课65 —— ATM + 购物车 架构
    Python正课64 —— re模块
    Python正课63 —— logging模块
    Python正课62 —— subprocess模块
  • 原文地址:https://www.cnblogs.com/hbyer/p/10060688.html
Copyright © 2011-2022 走看看