【洛谷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  阅读(146)  评论(0)    收藏  举报