[bzoj3529][Sdoi2014]数表_树状数组_莫比乌斯反演

数表 bzoj-3529 Sdoi-2014

题目大意:n*m的数表,第i行第j列的数是同时整除i和j的所有自然数之和。给定a,求数表中所有不超过a的和。

注释:$1\le n,m \le 10^5$。


想法:我们先不考虑那个a的限制:我们设f(i)表示整除i的自然数之和。

     $\sum\limits_{i=1}^n\sum\limits_{j=1}^m f(gcd(i,j))$

$=\sum\limits_{i=1}^n\sum\limits_{j=1}^m f(d)\cdot [gcd(i,j)==d]$

$=\sum\limits_{d=1}^n f(d)\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{d}\rfloor}[gcd(i,j)==1]$

$=\sum\limits_{d=1}^n f(d)\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{d}\rfloor}\sum\limits_{e|i,e|j} \mu(e)$

$=\sum\limits_{d=1}^n f(d)\sum\limits_{e=1}^{\lfloor\frac{n}{d}\rfloor}\mu(e)\sum\limits_{i=1}^{\lfloor\frac{n}{de}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{de}\rfloor}$

$=\sum\limits_{D=1}^n \sum\limits_{d|D} f(d)\cdot \mu(\frac{D}{d})sum(\lfloor\frac{n}{D}\rfloor)sum(\lfloor\frac{m}{D}\rfloor)$

然后,显然$f$函数是积性函数,$\mu$函数是积性函数,所以$f$和$\mu$的狄利克雷卷积$f\cdot \mu$是积性函数,所以不限制的问题就解决了。

那我们考虑限制怎么办?其实也非常简单。我们只需要在树状数组上维护出小于a的f,查询即可。

最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define inf 2147483647
using namespace std;
typedef long long ll; 
const int N=100010;
int mu[N],e[N],ans[N],c[N],vis[N],p[N],t[N],g[N];
struct F{int d,num;}f[N];
struct Q{int n,m,a,id;}q[N];
inline bool cmpT(Q a,Q b){return a.a<b.a;}
inline bool cmpt(F a,F b){return a.d<b.d;}
inline int lowbit(int x){return x&-x;}
int power(int a,int b)
{
	int res=1;
	while(b)
	{
		if (b&1) res*=a;
		a*=a;
		b>>=1;
	}
	return res;
}
void add(int x,int val)
{
	for(int i=x;i<N;i+=lowbit(i)) c[i]+=val;
}
int query(int x)
{
	int s=0;
	for(int i=x;i;i-=lowbit(i)) s+=c[i];
	return s;
}
int main()
{
	mu[1]=1;f[1].d=f[1].num=1;
	for(int i=2;i<N;i++)
	{
		f[i].num=i;
		if(!vis[i]) mu[i]=-1,f[i].d=t[i]=1+i,g[i]=1,p[++p[0]]=i;
		for(int j=1;j<=p[0] && i*p[j]<N;j++)
		{
			vis[i*p[j]]=1;
			if(i%p[j]==0)
			{
				mu[i*p[j]]=0;
				g[i*p[j]]=g[i]+1;
				t[i*p[j]]=t[i]+power(p[j],g[i]+1);
				f[i*p[j]].d=f[i].d/t[i]*t[i*p[j]];
				break;
			}
			else
			{
				mu[i*p[j]]=-mu[i];
				f[i*p[j]].d=f[i].d*f[p[j]].d;
				g[i*p[j]]=1;t[i*p[j]]=p[j]+1;
			}
		}
	}
	int T; scanf("%d",&T);
	for(int i=1;i<=T;i++) scanf("%d%d%d",&q[i].n,&q[i].m,&q[i].a),q[i].id=i;
	sort(q+1,q+1+T,cmpT);
	sort(f+1,f+N,cmpt);
	for(int now=0,i=1;i<=T;i++)
	{
		while(now+1<N && f[now+1].d<=q[i].a)
		{
			now++;
			for(int j=1;j*f[now].num<N;j++)
			{
				add(j*f[now].num,mu[j]*f[now].d);
			}
		}
		int n=q[i].n,m=q[i].m;
		if(n>m) swap(n,m);
		for(int j=1,k;j<=n;j=k+1)
		{
			k=min(n/(n/j),m/(m/j));
			ans[q[i].id]+=(n/j)*(m/j)*(query(k)-query(j-1));
		}
		ans[q[i].id]&=inf;
	}
	for(int i=1;i<=T;i++) printf("%d\n",ans[i]);
	return 0;
}

小结:这就是典型的拟对象的题,我们通过先构造拟对象,然后向完全对象转化,非常巧妙。

posted @ 2018-07-27 22:35  JZYshuraK_彧  阅读(216)  评论(0编辑  收藏  举报