51Nod1584 加权约数和

这题其实就是反演一波就好了(那你还推了一下午+一晚上),不过第一次碰到\(O(n\log n)\)预处理分块和式的方法……
不知为啥我跟唐教主的题解推的式子不太一样……(虽然本质上可能是相同的吧)
那就写一写好了,顺便骗点访问量(逃

\[\begin{align} \nonumber\text{Let}\space A=&\sum_{i=1}^n\sum_{j=1}^i i\sigma_1(ij),B=\sum_{i=1}^n i\sigma_1(i^2)\\ \nonumber\text{Then}\space Ans=&2A-B\\ \end{align}\]

\[\begin{align} \nonumber A=&\sum_{i=1}^n\sum_{j=1}^i i\sigma_1(ij)\\ \nonumber =&\sum_{i=1}^n\sum_{j=1}^i i\sum_{p|i}\sum_{q|j}[(p,q)=1]\frac{pj}q\\ \nonumber =&\sum_{d=1}^n\mu(d)\sum_{i=1}^n\sum_{j=1}^i i\sum _{p|i}\sum_{q|j}[d|(p,q)]\frac{pj}q\\ \nonumber =&\sum_{d=1}^n\mu(d)\sum_{d|p}p\left(\sum_{p|i}i\right)\sum_{d|q}\sum_{q|j}^i \frac j q\\ \nonumber =&\sum_{d=1}^n\mu(d)\sum_{p=1}^{\left\lfloor\frac n d\right\rfloor}dp\left(\sum_{p|i}^{\left\lfloor\frac n d\right\rfloor}di\right)\sum_{q=1}^{\left\lfloor\frac n d\right\rfloor}\sum_{q|j}^i \frac{dj}{dq}\\ \nonumber =&\sum_{d=1}^n\mu(d)\sum_{p=1}^{\left\lfloor\frac n d\right\rfloor}dp\left(\sum_{p|i}^{\left\lfloor\frac n d\right\rfloor}di\right)\sum_{q=1}^i\sum_{q|j}^i \frac j q\\ \nonumber =&\sum_{d=1}^n\mu(d)d^2\sum_{i=1}^{\left\lfloor\frac n d\right\rfloor}i\left(\sum_{p|i}p\right)\sum_{j=1}^i\sum_{q|j}\frac j q\\ \nonumber =&\sum_{d=1}^n\mu(d)d^2\sum_{i=1}^{\left\lfloor\frac n d\right\rfloor}i\sigma_1(i)\sum_{j=1}^i\sigma_1(j)\\ \nonumber =&\sum_{d=1}^n\mu(d)d^2\sum_{i=1}^{\left\lfloor\frac n d\right\rfloor}i\sigma_1(i)S_{\sigma_1}(i) \end{align}\]

\[\begin{align} \nonumber B=&\sum_{i=1}^n i\sigma_1(i^2)\\ \nonumber =&\sum_{i=1}^n i\sum_{p|i}\sum_{q|j}[(p,q)=1]\frac{pi}q\\ \nonumber =&\sum_{d=1}^n\mu(d)\sum_{i=1}^n i\sum_{p|i}\sum_{q|j}[d|(p,q)]\frac{pi}q\\ \nonumber =&\sum_{d=1}^n\mu(d)\sum_{d|i} i\sum_{d|p|i}\sum_{d|q|j}\frac{pi}q\\ \nonumber =&\sum_{d=1}^n\mu(d)d^2\sum_{i=1}^{\left\lfloor\frac n d\right\rfloor} i\sum_{p|i}\sum_{q|j}p\frac i q\\ \nonumber =&\sum_{d=1}^n\mu(d)d^2\sum_{i=1}^{\left\lfloor\frac n d\right\rfloor} i\sigma_1^2(i)\\ \end{align}\]

\[Ans=2A-B\\=\sum_{d=1}^n\mu(d)d^2\sum_{i=1}^{\left\lfloor\frac n d\right\rfloor}i\sigma_1(i)\left(2S_{\sigma_1}(i)-\sigma_1(i)\right) \]

到了这里就可以用\(O(n)\)预处理+单次询问\(O(\sqrt n)\)的经典做法了,总复杂度\(O(n+T\sqrt n)\)
但是这样还是有点慢……常数优化到一定程度之后极限数据仍然要跑2s+(卡常技巧不行……逃),看来常数优化玩不了了,只能试试别的做法。
考虑枚举\(d\)\(k=\left\lfloor\frac n d\right\rfloor\),并考虑它们能对哪些\(n\)作出贡献。不难发现,如果\(\left\lfloor\frac n d\right\rfloor=k\),那么一定有\(n\in[dk,d(k+1))\),再稍加观察就能得到每对\((d,k)\)都会对\(\ge dk\)的所有\(n\)作出贡献,因此枚举所有\(d,k\)并差分一下,最后求一遍前缀和即可。
这样就能做到预处理\(O(n\log n)\),询问\(O(1)\)了,不用卡常也可以过。

#pragma GCC optimize("Ofast")
#include<stdio.h>
#define int unsigned
using namespace std;
template<class T>inline void readint(T &v){
	int x=0;
	char c=getchar();
	while(c<48)c=getchar();
	while(c>47){
		x=x*10+(c^48);
		c=getchar();
	}
	v=x;
}
template<class T>inline void writeint(T x){
	static int a[25];
	if(!x)putchar('0');
	else{
		int i=0;
		while(x){
			a[++i]=x%10;
			x/=10;
		}
		while(i)putchar(a[i--]^48);
	}
}
const int maxn=1000010,p=1000000007;
void get_table(int);
bool notp[maxn];
int prime[maxn],mu[maxn],sigma[maxn],ans[maxn],f[maxn];
signed main(){
	get_table(1000000);
	int T;
	readint(T);
	for(int k=1;k<=T;k++){
		int n;
		readint(n);
		putchar('C');
		putchar('a');
		putchar('s');
		putchar('e');
		putchar(' ');
		putchar('#');
		writeint(k);
		putchar(':');
		putchar(' ');
		writeint(ans[n]);
		putchar('\n');
	}
	return 0;
}
void get_table(int n){
	mu[1]=sigma[1]=1;
	for(int i=2;i<=n;i++){
		if(!notp[i]){
			prime[++prime[0]]=i;
			mu[i]=-1;
			sigma[i]=i+1;
			f[i]=1;
		}
		for(int j=1;j<=prime[0]&&i*prime[j]<=n;j++){
			notp[i*prime[j]]=true;
			if(i%prime[j]){
				mu[i*prime[j]]=-mu[i];
				sigma[i*prime[j]]=(long long)sigma[i]*(prime[j]+1)%p;
				f[i*prime[j]]=i;
			}
			else{
				if(f[i]==1)sigma[i*prime[j]]=((long long)prime[j]*sigma[i]+1)%p;
				else sigma[i*prime[j]]=(long long)sigma[i/f[i]*prime[j]]*sigma[f[i]]%p;
				f[i*prime[j]]=f[i];
			}
		}
	}
	for(int i=2,sum=1;i<=n;i++){
		sum+=sigma[i];
		if(sum>=p)sum-=p;
		sigma[i]=(long long)i*sigma[i]%p*(2ll*sum-sigma[i]+p)%p;
	}
	for(int i=1;i<=n;i++){
		int t=((long long)i*i%p*(signed)mu[i]+p)%p;
		for(int j=1;i*j<=n;j++)ans[i*j]=(ans[i*j]+(long long)t*sigma[j])%p;
	}
	for(int i=2;i<=n;i++){
		ans[i]+=ans[i-1];
		if(ans[i]>=p)ans[i]%=p;
	}
}
posted @ 2017-07-02 06:26  AntiLeaf  阅读(351)  评论(0编辑  收藏  举报