牛客周赛 Round 35 小红的子序列权值和

文章讲述了如何计算长度为n的数组中,由1,2,3组成的所有子序列的贡献和,其中贡献定义为子序列乘积的因子数目。关键点在于考虑1的个数对因子数目的影响,以及利用组合数和阶乘计算整数的贡献。
摘要由CSDN通过智能技术生成

原题链接:G-小红的子序列权值和

题目大意:给长度为n的数组,数组由1,2,3,三种数构成,求所有子序列的贡献和是多少,子序列的贡献定义为子序列乘积的因子数目。

思路:可以观察到每一个子序列的乘积都是由1^x*2^y*3^z构成,那么对于这样的数他的因子就是(y+1)*(z+1),判断一个整数因子数目的时候1的个数并不重要。那么对于题目里面的子序列乘积也就是选几个2和选几个3的问题了,虽然1对因子数目没有影响,但是1会对子序列产生影响,例如说对于整数12,数组有2个1的情况下,12可以是2*2*3,也可以是2*2*3*1,也可以是2*2*3*1*1,也可以是2*2*3*1(二个1位置不一样所以子序列也不一样),所以对于每个整数产生的共享还需要乘以2的x次方,x为1的个数。因为数组里面可能有多个2和3,那么就需要使用组合数来计算一个整数的贡献,例如12,如果数组里面有3个2,那么就需要乘上C(3,2)了。

因为题目的数据范围是1e5那么就不可以同时枚举选2和选3的数量了,对于数组的全部贡献可以表示为C(cnt2,1)*(1+1)*C(cnt3,1)*(1+1)*ksm(2,cnt1)+C(cnt2,2)*(2+1)*C(cnt3,1)*(1+1)*ksm(2,cnt1)+......,那么就可以先算2的贡献,再算3的贡献最后相乘。

#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
const int N=1e6+10,mod=1e9+7;
ll t[10];//记录1,2,3的数量
ll p[N];//1到n的阶乘
ll ksm(ll a,ll b)
{
    ll sum=1;
    do
    {
        if(b&1)sum*=a;
        a*=a;a%=mod;sum%=mod;b>>=1;
    }while(b);
    return sum;
}
ll c(ll a,ll b)//逆元求组合数
{
    return p[a]%mod*ksm(p[b],mod-2)%mod*ksm(p[a-b],mod-2)%mod;
}
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    ll n;cin>>n;
    p[0]=1;
    ll ans1=0,ans2=0,temp=1;
    for(int i=1;i<N;i++)
    {
        p[i]=p[i-1]*i;p[i]%=mod;
    }
    for(int i=0;i<n;i++)
    {
        ll x;cin>>x;
        t[x]++;
    }
    for(int i=0;i<=t[2];i++)//先算2
    {
        ans1=ans1%mod+c(t[2],i)%mod*(i+1)%mod;ans1%=mod;
    }
    for(int i=0;i<=t[3];i++)//算3
    {
        ans2=ans2%mod+c(t[3],i)%mod*(i+1)%mod;ans1%=mod;
    }
    cout<<(ans1%mod*ans2%mod*ksm(2,t[1])%mod-1+mod)%mod;
    return 0;
}

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值