zoukankan      html  css  js  c++  java
  • 51Nod1584 加权约数和

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

    [egin{align} onumber ext{Let}space A=&sum_{i=1}^nsum_{j=1}^i isigma_1(ij),B=sum_{i=1}^n isigma_1(i^2)\ onumber ext{Then}space Ans=&2A-B\ end{align}]

    [egin{align} onumber A=&sum_{i=1}^nsum_{j=1}^i isigma_1(ij)\ onumber =&sum_{i=1}^nsum_{j=1}^i isum_{p|i}sum_{q|j}[(p,q)=1]frac{pj}q\ onumber =&sum_{d=1}^nmu(d)sum_{i=1}^nsum_{j=1}^i isum _{p|i}sum_{q|j}[d|(p,q)]frac{pj}q\ onumber =&sum_{d=1}^nmu(d)sum_{d|p}pleft(sum_{p|i}i ight)sum_{d|q}sum_{q|j}^i frac j q\ onumber =&sum_{d=1}^nmu(d)sum_{p=1}^{leftlfloorfrac n d ight floor}dpleft(sum_{p|i}^{leftlfloorfrac n d ight floor}di ight)sum_{q=1}^{leftlfloorfrac n d ight floor}sum_{q|j}^i frac{dj}{dq}\ onumber =&sum_{d=1}^nmu(d)sum_{p=1}^{leftlfloorfrac n d ight floor}dpleft(sum_{p|i}^{leftlfloorfrac n d ight floor}di ight)sum_{q=1}^isum_{q|j}^i frac j q\ onumber =&sum_{d=1}^nmu(d)d^2sum_{i=1}^{leftlfloorfrac n d ight floor}ileft(sum_{p|i}p ight)sum_{j=1}^isum_{q|j}frac j q\ onumber =&sum_{d=1}^nmu(d)d^2sum_{i=1}^{leftlfloorfrac n d ight floor}isigma_1(i)sum_{j=1}^isigma_1(j)\ onumber =&sum_{d=1}^nmu(d)d^2sum_{i=1}^{leftlfloorfrac n d ight floor}isigma_1(i)S_{sigma_1}(i) end{align}]

    [egin{align} onumber B=&sum_{i=1}^n isigma_1(i^2)\ onumber =&sum_{i=1}^n isum_{p|i}sum_{q|j}[(p,q)=1]frac{pi}q\ onumber =&sum_{d=1}^nmu(d)sum_{i=1}^n isum_{p|i}sum_{q|j}[d|(p,q)]frac{pi}q\ onumber =&sum_{d=1}^nmu(d)sum_{d|i} isum_{d|p|i}sum_{d|q|j}frac{pi}q\ onumber =&sum_{d=1}^nmu(d)d^2sum_{i=1}^{leftlfloorfrac n d ight floor} isum_{p|i}sum_{q|j}pfrac i q\ onumber =&sum_{d=1}^nmu(d)d^2sum_{i=1}^{leftlfloorfrac n d ight floor} isigma_1^2(i)\ end{align}]

    [Ans=2A-B\=sum_{d=1}^nmu(d)d^2sum_{i=1}^{leftlfloorfrac n d ight floor}isigma_1(i)left(2S_{sigma_1}(i)-sigma_1(i) ight) ]

    到了这里就可以用(O(n))预处理+单次询问(O(sqrt n))的经典做法了,总复杂度(O(n+Tsqrt n))
    但是这样还是有点慢……常数优化到一定程度之后极限数据仍然要跑2s+(卡常技巧不行……逃),看来常数优化玩不了了,只能试试别的做法。
    考虑枚举(d)(k=leftlfloorfrac n d ight floor),并考虑它们能对哪些(n)作出贡献。不难发现,如果(leftlfloorfrac n d ight floor=k),那么一定有(nin[dk,d(k+1))),再稍加观察就能得到每对((d,k))都会对(ge dk)的所有(n)作出贡献,因此枚举所有(d,k)并差分一下,最后求一遍前缀和即可。
    这样就能做到预处理(O(nlog 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('
    ');
    	}
    	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;
    	}
    }
    
  • 相关阅读:
    PHP 使用 GET 传递数组变量
    Java实现 蓝桥杯 算法训练 数据交换
    Java实现 蓝桥杯 算法训练 数据交换
    Java实现 蓝桥杯 算法训练 数据交换
    Java实现 蓝桥杯 算法训练 景点游览
    Java实现 蓝桥杯 算法训练 景点游览
    Java实现 蓝桥杯 算法训练 景点游览
    Java实现 蓝桥杯 算法训练 二进制数数
    Java实现 蓝桥杯 算法训练 二进制数数
    Java实现 蓝桥杯 算法训练 二进制数数
  • 原文地址:https://www.cnblogs.com/hzoier/p/7105009.html
Copyright © 2011-2022 走看看