「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\)变为\(\lceil\frac{L}{K}\rceil\),把\(R\)变成\(\lfloor\frac{R}{K}\rfloor\)。
这样子容易得出,现在要求的就是在\([L,R]\)之间,选数\(N\)次使选出的数最大公约数为\(1\)的方案数。
\[
Ans=\sum_{i_{1\sim N}=L}^R[\gcd(i_{1\sim N})=1]\\sum_{i_{1\sim N}=L}^R\sum_{d|i_{1\sim N}}\mu(d)\=\sum_{d=1}^R\mu(d)(\lfloor\frac Rd\rfloor-\lfloor\frac{L-1}d\rfloor)^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\n",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://www.cnblogs.com/autoint/p/11110984.html

时间: 2024-10-29 00:44:18

「CQOI2015」选数的相关文章

LibreOJ2097 - 「CQOI2015」任务查询系统

Portal Description 给出\(n(n\leq10^5)\)个任务,和总时间范围\(m(m\leq10^5)\).每个任务有开始/结束时间\(s_i,e_i(1\leq s_i \leq e_i \leq m)\)和优先级\(p_i(p_i\leq10^9)\).接下来\(m\)个询问,每次询问在时刻\(t_i\)时优先级前\(k\)大的任务的优先级之和,若\(k\)大于此时正在进行的任务总数则输出此时优先级之和.其中\(\{t_m\}\)是\(1..m\)的一个排列. Solut

「CQOI2015」任务查询系统

「CQOI2015」任务查询系统 传送门 好像也是板子题??? 区间修改,单点查询,考虑差分. 然后每次查询时就直接在对应的主席树上二分即可. 参考代码: #include <cstdio> #include <vector> #define rg register #define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", st

bzoj3930【CQOI2015】选数

3930: [CQOI2015]选数 Time Limit: 10 Sec  Memory Limit: 512 MB Submit: 742  Solved: 376 [Submit][Status][Discuss] Description 我们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有(H-L+1)^N种方案.小z很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的N个整数都求一次最大公约数,以便进一步研究.然而他很快发现工作量太大了,于是向你寻求帮助.你的任务很

BZOJ 3930 【CQOI2015】 选数

题目链接:选数 MDZZ,这种SB题我都Wa了这么多发,彻底没救系列- 首先,我们可以发现1,如果我们选了两个不同的数,那么它们的\(\gcd\)不会超过\(r-l+1\).于是,我们可以设一个\(f_i\)表示任取\(n\)个数,它们的\(\gcd\)为\(ik\)的方案数,最后我们要的答案就是\(f_1\).我们考虑容斥一下,在求\(f_i\)的时候,先把\([l,r]\)中是\(ik\)倍数的数全部拿出来,然后任意选\(n\)个,这样选出来的数他们的\(\gcd\)一定是\(ik\)的倍数

「SCOI2009」windy数

传送门 Luogu 解题思路 数位 \(\text{DP}\) 设状态 \(dp[now][las][0/1][0/1]\) 表示当前 \(\text{DP}\) 到第 \(i\) 位,前一个数是 \(las\),有没有顶到上界,有没有前导零的答案. 转移十分显然. 细节注意事项 咕咕咕 参考代码 #include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #i

dtoi4375「BJOI2019」删数

题意: 对于任意一个数列,如果能在有限次进行下列删数操作后将其删为空数列,则称这个数列可以删空.一次删数操作定义如下: 记当前数列长度为 k,则删掉数列中所有等于 k 的数. 现有一个长度为 n 的数列 a,有 m 次修改操作,第 i 次修改后你要回答:经过 i 次修改后的数列 a,至少还需要修改几个数才可删空? 每次修改操作为单点修改或数列整体加一或数列整体减一. 题解:      如果一个我要删去大小为a的数,那么序列长度会变成a-h[a](h[a]为数值a出线的次数).那么我们意会一下这个

BZOJ 3930: [CQOI2015]选数

3930: [CQOI2015]选数 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1130  Solved: 532[Submit][Status][Discuss] Description 我们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有(H-L+1)^N种方案.小z很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的N个整数都求一次最大公约数,以便进一步研究.然而他很快发现工作量太大了,于是向你寻求帮助.你的任务很简

「递归」求第n个斐波纳契数

用「递归」方法求第n个斐波纳契数 1 #include<stdio.h> 2 long int dog(int p) 3 { 4 if(p>1) 5 return dog(p-1)+dog(p-2); 6 else if (p==1||p==0) 7 return 1; 8 } 9 int main() 10 { 11 printf("您要求第几个斐波纳契数:\n"); 12 int n; 13 scanf("%d",&n); 14 pri

BZOJ 3930: [CQOI2015]选数 递推

3930: [CQOI2015]选数 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=3930 Description 我们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有(H-L+1)^N种方案.小z很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的N个整数都求一次最大公约数,以便进一步研究.然而他很快发现工作量太大了,于是向你寻求帮助