解题报告-和谐(harmony.*)

和谐(harmony.*)

题目背景

和谐是对立事物之间在一定的条件下、具体、动态、相对、辩证的统一,是不同事物之间相同相成、相辅相成、相反相成、互助合作、互利互惠、互促互补、共同发展的关系。

题目描述

具体来说,教主喜欢 GCD,moreD喜欢最小值。所以,为了使事情变得和谐起来,我们一致决定:如果一个数列所有数字的GCD与所有数字的最小值相等的话,我们定义这个数列为 moreD-教主·和谐数列。

可是,事情如果如此简单就不科学了。有些人也决定出来捣乱,他喜欢把数值限制在某个闭区间 \([1,x]\) 内。于是我们也决定把他和谐进题目里面。

于是题目就变成了这副鬼模样:先由有些人给出数列 \(A\),你需要构造一个与数列 \(A\) 等长的数列 \(B\),使得 \(\forall i \in [1,n]\)\(1\leq B_i\leq A_i\)。并且这个数列 \(B\) 是 moreD-教主·和谐数列。

但是 CD 也出来了,他觉得构造数列 B 太脑残了,于是,你需要求出满足要求的数列B的方案数对 \(10^9+7\) 取模的值。

输入描述

输入第一行一个整数 \(N\)

第二行 \(N\) 个正整数,用空格隔开,表示数列 \(A\)

输出描述

仅一个整数,表示答案。

输入输出样例

输入样例#1

3
2 3 4

输出样例#1

20

提示/说明

数据范围

  • 对于 \(30\)% 的数据,总方案数 \(\leq 10^6\)
  • 对于 \(60\)% 的数据,\(1 \leq N,A_i \leq 10^3\)
  • 对于 \(100\)% 的数据,\(1 \leq N,A_i \leq 10^6\)

抽象化题面

给定一个有 \(N\) 个元素的数列 \(A\),要求构造一个数列 \(B\),使得 \(\forall i \in [1,N]\),都有 \(1 \leq B_i \leq A_i\),并且 \(\gcd(B_i)=\min(B_i)\),求出满足条件的方案总数,对 \(10^9+7\) 取模。


解题报告

一道可爱的数学题。

显然,我们每确定一个 \(\min_{i\in[1,n]}(B_i)\),就可以算出当前 \(min(B_i)\) 所有符合条件的方案数。

具体怎么算?简单的乘法原理。设 \(M=\min_{i\in[1,n]}(B_i)\),根据提议题意,对于任意一个 \(A_i\),只有当对应的 \(B_i \leq A_i\)\(\lfloor \dfrac{B_i}{M} \rfloor \geq 1\) 时才合法,这样的方案只有 \(\lfloor \dfrac{A_i}{M} \rfloor\) 个,同时还有注意,要记得排除 \(\gcd(B_i)=k \times M(k>1,k\in N)\) 的情况,这部分的方案数是 \(\lfloor \dfrac{A_i}{M} \rfloor-1\) 个,那么总的方案数就是 \(\prod_{i \in [1,n]} ( \lfloor \dfrac{A_i}{M} \rfloor)-\prod_{i \in [1,n]} ( \lfloor \dfrac{A_i}{M} \rfloor-1)\)。那么就可以枚举所有不大于 \(\max_{i \in [1,n]}(A_i)\)\(M\),计算方案数在累加。

但是显然,直接计算 \(\prod_{i \in [1,n]} ( \lfloor \dfrac{A_i}{M} \rfloor)-\prod_{i \in [1,n]} ( \lfloor \dfrac{A_i}{M} \rfloor-1)\) 的复杂度是不可接受的,我们得找个方法快速计算。

有一个常用的思路,由于存在向下取整,那么很多数的贡献其实是相同的,我们可以这样的数归为一类来计算。假设有 \(k\)\(A_i\),它们的 \(\lfloor \dfrac{A_i}{M} \rfloor\) 相同,设为 \(r\),那么它们提供的总方案数就是 \(r^k-(r-1)^k\),那么我们只需要计算出每一个对 \(r\)\(k\),就可以用快速幂算法统计总贡献了。

对于 \(k\),我们可以直接把每一个数丢进一个计数桶 \(cnt\),并对这个桶做一次前缀和,那么权值在区间 \([l,r]\)\(A_i\) 的个数就为 \(cnt_r-cnt_{l-1}\)

对于 \(r\),既然我们已经可以求出任意权值区间的数的个数,就枚举每一个可能的向下取整的结果,并求出对应的权值区间就好了。

具体的,如果某一个 \(A_i\) 满足 \(\lfloor \dfrac{A_i}{M} \rfloor=X\),那么就有 \(A_i \in [M \times X,(M+1)\times X-1]\)。所以就可以求出向下取整的结果为 \(X\)\(A_i\) 个数 \(cnt_{(M+1) \times X-1}-cnt_{M \times X-1}\) 了。

算法的时间复杂度不难么证明为 \(O( \min(A_i)\times\log(\min(A_i)) \times \log(n) )\),代码如下:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int INF=0x3f3f3f3f;
const int mod=(1E+9)+7;
const int N=101100;

#define ckmax(x,y) ( x=max(x,y) )
#define ckmin(x,y) ( x=min(x,y) )

inline int read()
{
	int f=1,x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch))  { x=x*10+ch-'0';    ch=getchar(); }
	return f*x;
}

int n,m=INF,L;
int a[N],cnt[N];

inline int FastPow(int x,int k)
{
    int tmp=1; x%=mod;
    while(k)
    {
        if(k&1) tmp=(tmp*x)%mod;
        x=(x*x)%mod;
        k>>=1;
    }
    return tmp%mod;
}

signed main()
{
	freopen("harmony.in","r",stdin);
	freopen("harmony.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        ckmax(L,a[i]);
        ckmin(m,a[i]);
        cnt[a[i]]++;
    }
    for(int i=1;i<=L;i++)
      cnt[i]+=cnt[i-1];

    int ans=0;
    for(int i=m;i>=1;i--)
    {
        int tmp1=1,tmp2=1;
        for(int j=1;j<=L/i;j++)
        {
            int l=j*i,r=min(L,(j+1)*i-1);
            int sum=cnt[r]-cnt[l-1];
            if(!sum) continue;
            tmp1=tmp1*FastPow(j,sum) %mod;
            tmp2=tmp2*FastPow(j-1,sum) %mod;
        }
        ans=(ans+tmp1-tmp2+mod)%mod;
    }
    printf("%lld",ans);
	return 0;
}
posted @ 2025-09-25 22:16  南北天球  阅读(8)  评论(0)    收藏  举报