• 「CQOI2015」选数


    「CQOI2015」选数

    题目描述

    我们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有(H-L+1)^N种方案。小z很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的N个整数都求一次最大公约数,以便进一步研究。然而他很快发现工作量太大了,于是向你寻求帮助。你的任务很简单,小z会告诉你一个整数K,你需要回答他最大公约数刚好为K的选取方案有多少个。由于方案数较大,你只需要输出其除以1000000007的余数即可。

    输入输出格式

    输入格式:

    输入一行,包含4个空格分开的正整数,依次为N,K,L和H。

    输出格式:

    输出一个整数,为所求方案数。

    输入输出样例

    输入样例#1: 复制
    2 2 2 4
    输出样例#1: 复制
    3

    说明

    样例解释

    所有可能的选择方案:(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2), (4, 3), (4, 4)

    其中最大公约数等于2的只有3组:(2, 2), (2, 4), (4, 2)

    对于100%的数据,1<=N,K<=10^9,1<=L<=H<=10^9,H-L<=10^5

    杜教筛题解

    首先,将gcd为K进行经典转化:把(L)变为(lceilfrac{L}{K} ceil),把(R)变成(lfloorfrac{R}{K} floor)
    这样子容易得出,现在要求的就是在([L,R])之间,选数(N)次使选出的数最大公约数为(1)的方案数。

    [Ans=sum_{i_{1sim N}=L}^R[gcd(i_{1sim N})=1]\ sum_{i_{1sim N}=L}^Rsum_{d|i_{1sim N}}mu(d)\ =sum_{d=1}^Rmu(d)(lfloorfrac Rd floor-lfloorfrac{L-1}d floor)^N ]

    然后杜教筛加整除分块即可。时间复杂度(O((frac RK)^frac 23+sqrt{R}log N))

    #include<bits/stdc++.h>
    #define il inline
    #define co const
    template<class T>T read(){
        T data=0,w=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
        for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
        return data*w;
    }
    template<class T>il T read(T&x) {return x=read<T>();}
    typedef long long LL;
    
    co int N=1e6+1;
    int pri[N],tot,mu[N];
    void init(){
    	pri[1]=mu[1]=1;
    	for(int i=2;i<N;++i){
    		if(!pri[i]) pri[++tot]=i,mu[i]=-1;
    		for(int j=1;j<=tot&&i*pri[j]<N;++j){
    			pri[i*pri[j]]=1;
    			if(i%pri[j]==0){
    				mu[i*pri[j]]=0;
    				break;
    			}
    			mu[i*pri[j]]=-mu[i];
    		}
    	}
    	for(int i=2;i<N;++i) mu[i]+=mu[i-1];
    }
    std::map<int,int> smu;
    int Mu(int n){
    	if(n<N) return mu[n];
    	if(smu.count(n)) return smu[n];
    	int ans=1;
    	for(int l=2,r;l<=n;l=r+1){
    		r=n/(n/l);
    		ans-=(r-l+1)*Mu(n/l);
    	}
    	return smu[n]=ans;
    }
    co int mod=1e9+7;
    il int add(int a,int b){
    	return (a+=b)>=mod?a-mod:a;
    }
    il int mul(int a,int b){
    	return (LL)a*b%mod;
    }
    int fpow(int a,int b){
    	int ans=1;
    	for(;b;b>>=1,a=mul(a,a))
    		if(b&1) ans=mul(ans,a);
    	return ans;
    }
    int main(){
    	init();
    	int n=read<int>(),k=read<int>();
    	int L=(read<int>()-1)/k+1,R=read<int>()/k;
    	int ans=0;
    	for(int l=1,r;l<=R;l=r+1){
    		if((L-1)/l) r=std::min((L-1)/((L-1)/l),R/(R/l)); // edit 1:/0
    		else r=R/(R/l);
    		ans=add(ans,add(mod,mul(Mu(r)-Mu(l-1),fpow(R/l-(L-1)/l,n))));
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    注意:整除分块的时候,因为是以R为上限,所以L-1那坨可能出现除以0的情况,需要特判。

    xyz32768的容斥题解

    现在要求的就是在([L,H])之间,选数(N)次使选出的数最大公约数为(1)的方案数。

    现在,用(f[i])表示选出的数的最大公约数(i)且选出的数不全相同的方案数。此时先求出([L,H])之间(i)的倍数的个数(x),暂时令(f[i]=x^N-x)

    但此时得到的(f[i])实际上是含有公约数(i)的方案数,不是最大公约数为(i)的方案数。但是可以发现,此时的(f[i])包含有最大公约数为(i,2i,3i,...)的方案数。这时候使用容斥原理:假设已经知道了(f[2i],f[3i],...)的最终结果,那么就把(f[i])分别减去(f[2i],f[3i],...),就可以得到(f[i])的最终结果。倒着推一遍。

    特殊情况:(L=1)时可以所有的数都选(1)。所以(L=1)时答案要加(1)

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    inline int read() {
        int res = 0; bool bo = 0; char c;
        while (((c = getchar()) < '0' || c > '9') && c != '-');
        if (c == '-') bo = 1; else res = c - 48;
        while ((c = getchar()) >= '0' && c <= '9')
            res = (res << 3) + (res << 1) + (c - 48);
        return bo ? ~res + 1 : res;
    }
    const int N = 1e5 + 5, PYZ = 1e9 + 7;
    int n, K, L, H, f[N];
    int qpow(int a, int b) {
        int res = 1;
        while (b) {
            if (b & 1) res = 1ll * res * a % PYZ;
            a = 1ll * a * a % PYZ;
            b >>= 1;
        }
        return res;
    }
    int main() {
        int i, j; n = read(); K = read(); L = read(); H = read();
        if (L % K) L = L / K + 1; else L /= K; H /= K;
        if (L > H) return puts("0"), 0;
        for (i = 1; i <= H - L; i++) {
            int l = L, r = H;
            if (l % i) l = l / i + 1; else l /= i; r /= i;
            if (l > r) continue;
            f[i] = (qpow(r - l + 1, n) - (r - l + 1) + PYZ) % PYZ;
        }
        for (i = H - L; i; i--) for (j = (i << 1); j <= H - L; j += i)
            f[i] = (f[i] - f[j] + PYZ) % PYZ;
        if (L == 1) (f[1] += 1) %= PYZ; cout << f[1] << endl;
        return 0;
    }
    
  • 相关阅读:
    批处理文件双击运行成功,程序调用却运行失败解决方案
    前端安全之加解密种类与HTTPS加密原理(二)
    node包管理nvm与pnpm(一)
    React状态管理—reduxAPI原理分析(三)
    动态规划原理与算法实践(二)
    双指针算法基本原理和实践(一)
    分治算法基本原理和实践(三)
    编程范式(一)
    Java基础
    前端模块化CommonJS、AMD、CMD、ES6模块(二)
  • 原文地址:https://www.cnblogs.com/autoint/p/11110984.html
Copyright © 2020-2023  润新知