解题报告-和谐(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;
}