CF839D Winter is here 题解
思路
对于这个数组的每个区间分别做\(\gcd\),然后再求贡献,这样显然不行...怎么办?我们就从\(\gcd\)入手,我们枚举\(\gcd\),然后求助每个可行的\(\gcd\)在这个数组中的贡献是多少。
现在我们设置几个数组:
\(cnt_i\)表示在原来的数组中\(i\)出现的次数,\(dp_i\)表示所有\(\gcd\)为\(i\)的区间的总长度(由乘法分配律可知,每个区间的长度合并之后对于整体贡献的影响不会改变)
如果说当前我们求出了\(i\)的倍数的个数\(res\),那么它的\(dp\)值就是:
\(\sum\limits_{i=1}^{res}\tbinom{res}{i}\times i\)
=\(\sum\limits_{i=1}^{res}\frac{res!\times i}{i!(res-i)!}\)
=\(res\times \sum\limits_{i=1}^{res}\frac{(res-1)!}{(i-1)!(res-i)!}\)
=\(res\times \sum\limits_{i=0}^{res-1}\tbinom{res-1}{i}\)
由二项式定理(或者组合数的意义)可以得到:
=\(res*2^{res-1}\)
我们就可以愉快的求解了!
\(ACcode\)
#include<bits/stdc++.h>
#define F(i,l,r) for(register int i=l;i<=r;i++)
#define D(i,r,l) for(register int i=r;i>=l;i--)
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define p_b push_back
#define m_p make_pair
#define il inline
const int INF=0x3f3f3f3f,mod=1e9+7,N=1e6+5;
using namespace std;
int cnt[N],fac[N]={1},n;
int ans,dp[N],a[N],maxn;
int main() {
scanf("%d",&n);
F(i,1,n) fac[i]=fac[i-1]*2ll%mod;//预处理出2^i
F(i,1,n) {
scanf("%d",&a[i]);
maxn=max(maxn,a[i]);//求出枚举i(gcd)的范围
cnt[a[i]]++;
}
D(i,maxn,2) {
int res=0;
F(j,i,maxn) res+=cnt[j],j+=i-1;//res意义如上文。这里j其实就是+=i了,奇怪的写法不要在意
if(res) {
dp[i]=fac[res-1]*1ll*res%mod;//公式
F(j,2*i,maxn) dp[i]=(dp[i]+mod-dp[j])%mod,j+=i-1;//这里就是说把多出来的贡献减掉,因为我们只要i的贡献就好了i的倍数的都不要。记住+mod防止负数!
ans=(ans+dp[i]*1ll*i%mod)%mod;//求出贡献
}
}
printf("%d\n",ans);//输出
return 0;
}

浙公网安备 33010602011771号