[cf839d]Winter is here容斥原理

本文解析了一道算法题目,即求一个数列所有子序列的权值和,权值定义为子序列长度乘以其最大公约数(当最大公约数大于1)。介绍了两种解法:一是使用容斥原理,二是利用莫比乌斯反演。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给定一个数列${a_i}$,若子序列长度为$k$,最大公约数为$gcd$,定义子序列的权值为$k*\gcd (\gcd  > 1)$。求所有子序列的权值和。 答案对10^9+7取模。

解题关键:容斥原理求序列中各$gcd$的个数,亦可用莫比乌斯函数。

逆序求的话,前面直接减后面的个数,在后面一项就相当于相加了,如此往复。

关于知道所有$gcd$为$n$的个数之后答案的求法:

法一:

$\begin{array}{l}
1C_n^1 + 2C_n^2 + ... + nC_n^n\\
= n(C_{n - 1}^1 + C_{n - 1}^2 + ... + C_{n - 1}^{n - 1})\\
= n{2^{n - 1}}
\end{array}$

法二:

$\begin{array}{l}
[{(x + 1)^n}]' = n{(x + 1)^{n - 1}}\\
{(x + 1)^n} = \sum\limits_{i = 1}^n {C_n^i{x^i}} \\
n{(x + 1)^{n - 1}} = \sum\limits_{i = 1}^n {C_n^ii{x^{i - 1}}}
\end{array}$

法三:逆序相加

容斥解法:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int mod=1e9+7;
 5 #define inf 0x3f3f3f3f
 6 ll c[1000004],pw[1000007],sum[1000007];
 7 int main(){
 8     ll n,x,mx=-inf;
 9     cin>>n;
10     pw[0]=1;
11     for(int i=1;i<=n+3;i++) pw[i]=pw[i-1]*2%mod;
12     for(int i=0;i<n;i++) cin>>x,c[x]++,mx=max(mx,x);//hash一下 
13     ll ct;
14     ll ans=0;
15     for(int i=mx;i>1;i--){
16         ct=0;
17         for(int j=i;j<=mx;j+=i){
18             ct=(ct+c[j])%mod;
19             sum[i]-=sum[j];
20         }
21         sum[i]=(sum[i]+ct*pw[ct-1]%mod+mod)%mod; 
22         ans=(ans+sum[i]*i%mod+mod)%mod;
23     }
24     cout<<ans<<"\n";
25     return 0;
26 }

 莫比乌斯反演解法:

 1 #include<bits/stdc++.h>
 2 #define inf 0x3f3f3f3f
 3 using namespace std;
 4 typedef long long ll;
 5 const int mod=1e9+7;
 6 ll mu[1000020],c[1000060],cnt[1000050],pw[1000050],g[1000050];
 7 void sievemu(int n){
 8     mu[1]=1;
 9     for(int i=1;i<=n;i++){
10         for(int j=i+i;j<=n;j+=i){
11             mu[j]-=mu[i];
12         }
13     }
14 }
15 
16 int main(){
17     sievemu(1000005);
18     ll n,x,mx=-inf;
19     cin>>n;
20     pw[0]=1;
21     for(int i=1;i<=n;i++) pw[i]=pw[i-1]*2%mod;
22     for(int i=0;i<n;i++) cin>>x,c[x]++,mx=max(mx,x);
23     for(int i=2;i<=mx;i++){
24         ll ss=0;
25         for(int j=i;j<=mx;j+=i){
26             ss+=c[j];
27         }
28         if(ss) cnt[i]=ss*pw[ss-1]%mod;
29     }
30     //计算gcd倍数的个数
31      
32     //对答案进行莫比乌斯反演 
33     ll ans=0;
34     for(int i=2;i<=mx;i++){
35         for(int j=i;j<=mx;j+=i){
36             g[i]=(g[i]+cnt[j]*mu[j/i]%mod+mod)%mod;
37         }
38         ans=(ans+i*g[i]%mod+mod)%mod;
39     }
40     cout<<ans<<"\n";
41     return 0;
42 }

 

转载于:https://www.cnblogs.com/elpsycongroo/p/7398397.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值