zoukankan      html  css  js  c++  java
  • 洛谷 P4902 乘积 (约数筛,前缀和(积))

    洛谷P4902乘积

    题意简述:

    给 $ t $ 组 $ (a,b) $ 求:

    $ prod_{i=A}^{B}prod_{j=1}^{i}(frac{i}{j})^{lfloor frac{i}{j} floor} (mod 19260817) $



    $ solution: $

    考试都去想 $ T2 $ 了……

    题目是真的不错,首先看到题面我们可以想到三个角度:

    1. 预处理再回答
    2. 分子分母可以分开求
    3. 将询问拆成 $ (1,b)/(1,a-1) $ 于是可以默认从一开始

    然后我们先看分子, $ prod_{i=1}^{n}prod_{j=1}^{i}i^{lfloor frac{i}{j} floor} $ 这个东西我们可以先求出对于每一个 $ i $ 的 $ prod_{j=1}^{i}i^{lfloor frac{i}{j} floor} $ ,然会前缀积。对于每个 $ prod_{j=1}^{i}i^{lfloor frac{i}{j} floor} $ 我们可以考虑化简: $ i^{prod_{j=1}^{i} lfloor frac{i}{j} floor} $ 。这个指数我们观察法(或者将 $ i-1 $ 和 $ i $ 比较)可以发现和约数合数有关,并且就是约数前缀和。而约数前缀和是 $ nlogn $ 的,符合要求。

    然后我们看分母, $ prod_{i=1}^{n}prod_{j=1}^{i}(frac{1}{j})^{lfloor frac{i}{j} floor} $ ,这个同样可以先求出对于每一个 $ i $ 的 $ prod_{j=1}^{i}frac{1}{j} ^{lfloor frac{i}{j} floor} $ ,然会前缀积。其实 $ frac{1}{j} $ 可以通过预处理逆元来完成,实际上我们只需要知道求 $ prod_{j=1}^{i}j^{lfloor frac{i}{j} floor} $ 即可。这个东西我们将 $ i-1 $ 和 $ i $ 比较,可以发现每次 $ i+1 $ 这个式子就会乘上 $ i $ 的所有约数的乘积。用约数筛法可以递推得到。

    上述两个过程都可以在约数筛的同时一并完成,处理好逆元,还可以 $ O(1) $ 回答。



    $ code: $

    #include<iostream>
    #include<cstdio>
    #include<iomanip>
    #include<algorithm>
    #define ll long long
    #define db double
    #define rg register int
    
    using namespace std;
    
    const int mod=19260817;
    
    int t,n;
    int a[1000005]; //询问
    int b[1000005];
    int f[1000005]; //分子
    int g[1000005]; //分母
    int s[1000005]; //答案
    int inv[1000005];
    
    inline int qr(){
    	register char ch; register bool sign=0; rg res=0;
    	while(!isdigit(ch=getchar()))if(ch=='-')sign=1;
    	while(isdigit(ch))res=res*10+(ch^48),ch=getchar();
    	if(sign)return -res; else return res;
    }
    
    inline int ksm(ll x,int y){ //快速幂
    	rg res=1;
    	while(y){
    		if(y&1)res=res*x%mod;
    		x=x*x%mod; y>>=1;
    	}return res;
    }
    
    int main(){
    	t=qr(); inv[1]=1;
    	for(rg i=1;i<=t;++i){
    		a[i]=qr(),b[i]=qr();
    		n=max(n,max(a[i],b[i])); //求上界
    	}
    	for(rg i=2;i<=n;++i) inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod; //线性求逆元
    	for(rg i=0;i<=n;++i) g[i]=1; //初始化
    	for(rg i=1;i<=n;++i){
    		for(rg j=i;j<=n;j+=i)
    			++f[j],g[j]=(ll)g[j]*inv[i]%mod; //将这个数的贡献计入它的倍数里
    		f[i]+=f[i-1]; //约数个数前缀和
    		g[i]=(ll)g[i-1]*g[i]%mod; //约数前缀积
    	} f[0]=g[0]=s[0]=1;
    	for(rg i=1;i<=n;++i){
    		f[i]=ksm(i,f[i]); //计算逆元
    		f[i]=(ll)f[i-1]*f[i]%mod; //约数个数的前缀和的前缀积
    		g[i]=(ll)g[i-1]*g[i]%mod; //约数前缀积的前缀积
    		s[i]=(ll)f[i]*g[i]%mod; //计算1-i的答案
    	}
    	for(rg i=1;i<=t;++i){
    		rg x=a[i],y=b[i];
    		printf("%lld
    ",(ll)s[y]*ksm(s[x-1],mod-2)%mod); //(a~b)=(1~b)/(1~(a-1))
    	}
    	return 0;
    }
    
  • 相关阅读:
    Linux 中如何用源代码安装软件,以及如何卸载它
    Linux 中的 Install命令
    PHP 常用header头定义
    如何防止重复提交表单?
    如何从二维数组中的多个key中获取指定key的值?
    jquery的ajax全局事件详解
    PHP+MySQL分页显示示例分析
    javascript中的事件类型
    事件委托/事件代理,
    彻底弄懂JS的事件冒泡和事件捕获
  • 原文地址:https://www.cnblogs.com/812-xiao-wen/p/11708683.html
Copyright © 2011-2022 走看看