• 【洛谷P3312】数表


    题目

    题目链接:https://www.luogu.com.cn/problem/P3312
    有一张 \(n\times m\) 的数表,其第 \(i\) 行第 \(j\) 列(\(1\le i\le n\)\(1\le j\le m\))的数值为能同时整除 \(i\)\(j\) 的所有自然数之和。给定 \(a\),计算数表中不大于 \(a\) 的数之和。

    思路

    先不考虑 \(a\) 的限制,那么 \((i,j)\) 的数值即为 \(\gcd(i,j)\) 的因子之和(设为 \(g(i)\))。\(g(i)\) 可以 \(O(n\log n)\) 预处理出。

    \[ans=\sum^{n}_{i=1}g(i)\times \sum^{\min(n,m)}_{d|i}\mu(\frac{i}{d})\lfloor{\frac{n}{i}}\rfloor\lfloor{\frac{m}{i}}\rfloor \]

    \[=\sum^{n}_{i=1}\lfloor{\frac{n}{i}}\rfloor\lfloor{\frac{m}{i}}\rfloor\sum^{\min(n,m)}_{i|d}g(d)\mu(\frac{i}{d}) \]

    当有 \(a\) 的限制时,只有 \(g(x)\leq a\)\(g(x)\) 才可以产生贡献。所以我们将询问按 \(a\) 排序,数字 \(x\)\(g(x)\) 排序,对于一个询问 \(a\)\(g(x)\leq a\) 的所有 \(g(x)\)\(x\) 倍数的贡献加上,然后再询问一段区间的和。
    用树状数组处理即可。然后就是整除分块乱搞了。
    时间复杂度 \(O(Q\sqrt{n}+Q\log^2 n)\)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef unsigned int uint;
    
    const int N=100010;
    int Q,tot,prm[N],mu[N],g[N];
    uint ans[N];
    bool v[N];
    
    struct Query
    {
    	int n,m,a,id;
    }ask[N];
    
    struct node
    {
    	int g,id;
    }a[N];
    
    bool cmp1(Query x,Query y)
    {
    	return x.a<y.a;
    }
    
    bool cmp2(node x,node y)
    {
    	return x.g<y.g;
    }
    
    void findprm(int n)
    {
    	mu[1]=1;
    	for (int i=2;i<=n;i++)
    	{
    		if (!v[i])
    			prm[++tot]=i,mu[i]=-1;
    		for (int j=1;j<=tot;j++)
    		{
    			if (i>n/prm[j]) break;
    			v[prm[j]*i]=1; mu[prm[j]*i]=-mu[i];
    			if (!(i%prm[j]))
    			{
    				mu[i*prm[j]]=0;
    				break;
    			}
    		}
    	}
    }
    
    struct BIT
    {
    	uint c[N];
    	
    	void add(int x,uint v)
    	{
    		for (int i=x;i<N;i+=i&-i)
    			c[i]+=v;
    	}
    	
    	uint query(int x)
    	{
    		uint ans=0;
    		for (int i=x;i;i-=i&-i)
    			ans+=c[i];
    		return ans;
    	}
    }bit;
    
    int main()
    {
    	findprm(N-10);
    	for (int i=1;i<=N-10;i++)
    		for (int j=i;j<=N-10;j+=i)
    			g[j]+=i;
    	for (int i=1;i<=N-10;i++)
    		a[i]=(node){g[i],i};
    	sort(a+1,a+1+N-10,cmp2);
    	scanf("%d",&Q);
    	for (int i=1;i<=Q;i++)
    	{
    		scanf("%d%d%d",&ask[i].n,&ask[i].m,&ask[i].a);
    		ask[i].id=i;
    	}
    	sort(ask+1,ask+1+Q,cmp1);
    	for (int i=1,j=1;i<=Q;i++)
    	{
    		for (;j<=N-10 && a[j].g<=ask[i].a;j++)
    			for (int k=a[j].id;k<=N-10;k+=a[j].id)
    				bit.add(k,1U*mu[k/a[j].id]*a[j].g);
    		int n=ask[i].n,m=ask[i].m;
    		for (int l=1,r;l<=min(n,m);l=r+1)
    		{
    			r=min(n/(n/l),m/(m/l));
    			ans[ask[i].id]+=1U*(n/l)*(m/l)*(bit.query(r)-bit.query(l-1));
    		}
    	}
    	for (int i=1;i<=Q;i++)
    		printf("%d\n",(int)(2147483647U&ans[i]));
    	return 0;
    }
    
  • 相关阅读:
    python的变量,对象的内存地址以及参数传递过程
    win10环境pycharm社区版创建django项目
    组合,菱形继承,子类重用父类2,深度广度查找
    类内的函数共享给对象使用
    模块与面向对象初解
    正则模块,sys模块
    包介绍,与日记模块
    模块运用,文件搜索
    递归,匿名函数
    生成器与简写
  • 原文地址:https://www.cnblogs.com/stoorz/p/13736808.html
Copyright © 2020-2023  润新知