zoukankan      html  css  js  c++  java
  • P2522 [HAOI2011]Problem b 题解

    CSDN同步

    原题链接

    简要题意:

    [sum_{i=a}^b sum_{j=c}^d [gcd(i,j)==k] ]

    (T) 组询问.

    本题没有部分分,直接考虑一个式子:

    [sum_{i=1}^a sum_{j=1}^b [gcd(i,j)==k] ]

    [= sum_{i=1}^{lfloor frac{a}{k} floor} sum_{j=1}^{lfloor frac{b}{k} floor} [gcd(i,j)==1] ]

    [= sum_{i=1}^{lfloor frac{a}{k} floor} sum_{j=1}^{lfloor frac{b}{k} floor} sum_{d | gcd(i,j)} mu_d ]

    [= sum_{d=1}^{min(a,b)} mu_d lfloor frac{a}{dk} floor lfloor frac{b}{dk} floor ]

    上面每一步都是在用 莫比乌斯反演 的性质,不加解释。

    那么,我们只需要用 (O(n)) 的时间预处理 (mu),就可以用 整除分块(O(sqrt{min(a,b)})) 的时间内求解这样一个式子。

    那么,已知 (k),令:

    [f_{a,b} = sum_{i=1}^a sum_{j=1}^b [gcd(i,j)==k] ]

    则:

    [sum_{i=a}^b sum_{j=c}^d [gcd(i,j)==k] = f_{b,d} - f_{b,c-1} - f_{a-1,d} + f_{a-1,c-1} ]

    (可以稍微参考一下二维前缀和得出)

    所以我们以 (O(5 imes 10^4)) 预处理,(O(n sqrt{min(a,b)})) 询问解决了本题。

    时间复杂度:(O(a + nsqrt{a})).

    实际得分:(100pts).(需要适度卡常)

    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int N=1e6+1;
    
    inline ll read(){char ch=getchar(); ll f=1; while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
    	ll x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
    
    int prime[N],mu[N],sum[N];
    int cnt=0,k; bool h[N];
    
    inline void Euler(int n) {
    	mu[1]=1; for(register int i=2;i<=n;i++) {
    		if(!h[i]) mu[i]=-1,prime[++cnt]=i;
    		for(int j=1;j<=cnt && i*prime[j]<=n;j++) {
    			h[i*prime[j]]=1;
    			if(i%prime[j]==0) break;
    			mu[i*prime[j]]-=mu[i];
    		}
    	} for(int i=1;i<=n;i++) sum[i]=sum[i-1]+mu[i];
    } //预处理 mu 的前缀和
    
    inline int min(int a,int b) {return a<b?a:b;}
    inline ll calc(int a,int b) {
    	ll ans=0;
    	for(register int i=1;i<=min(a,b);) {
    		int t=min(a/(a/i),b/(b/i));
    		ans+=1ll*(a/k/i)*(b/k/i)*(sum[t]-sum[i-1]);
    		i=t+1;
    	} return ans; //整除分块
    }
    
    int main() {
    	Euler(N-1); int T=read(); while(T--) {
    		int a=read(),b=read(),c=read(),d=read(); k=read();
    		printf("%lld
    ",calc(b,d)-calc(b,c-1)-calc(a-1,d)+calc(a-1,c-1));
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    241. Different Ways to Add Parentheses java solutions
    89. Gray Code java solutions
    367. Valid Perfect Square java solutions
    46. Permutations java solutions
    116. Populating Next Right Pointers in Each Node java solutions
    153. Find Minimum in Rotated Sorted Array java solutions
    判断两颗树是否相同
    求二叉树叶子节点的个数
    求二叉树第k层的结点个数
    将二叉排序树转换成排序的双向链表
  • 原文地址:https://www.cnblogs.com/bifanwen/p/12818430.html
Copyright © 2011-2022 走看看