1. 程式人生 > >[bzoj3529][Sdoi2014]數表_樹狀數組_莫比烏斯反演

[bzoj3529][Sdoi2014]數表_樹狀數組_莫比烏斯反演

轉化 i++ mes += 給定 ostream 解決 style swa

數表 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;
}

小結:這就是典型的擬對象的題,我們通過先構造擬對象,然後向完全對象轉化,非常巧妙。

[bzoj3529][Sdoi2014]數表_樹狀數組_莫比烏斯反演