【洛谷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;
}
posted @ 2020-09-26 21:17  stoorz  阅读(97)  评论(0编辑  收藏  举报