AtCoder Regular Contest 116(A思维B贡献统计C组合数学(a[i]为a[i-1]的倍数的序列数))

每次做arc都感觉虽然很难但很有意思

A.Odd vs Even

第一个想法是包含2的次数有关。很容易想到,若这个数是奇数,那它一定没有偶数因子。再通过打表后我们可以发现:若2的次数只有1次,那么,奇数因子数=偶数因子数。若2的次数更大,那偶数因子数>奇数因子数。

signed main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	#endif
	//clock_t c1 = clock();
	//===========================================================
    read(t);
    while(t--){
        read(n);
        if(n&1){
            puts("Odd");   
        }
        else{
            if(n==2)puts("Same");
            else{
                int cnt=0;
                while(n%2==0)cnt++,n/=2;
                if(cnt>1)puts("Even");
                else puts("Same");
            }
        }
    }
	//===========================================================
	//std::cerr << "Time:" << clock() - c1 << "ms" << std::endl;
	return 0;
}

B.Products of Min-Max

算贡献的题目。
(1)、首先需要对只有一个数的子序列处理。先求出每个数的平方的和作为答案的一部分。

(2)、接下来考虑子序列长度大于1的情况:考虑对于每一对乘积a*b,它贡献的次数就是以a为最小值,b为最大值的子序列的个数。那么,就是考虑填入大小在a~b之间的数,那么,每个数都可以选择出现和不出现。
因此,假设a和b在排序后的数组中的下标分别为i和j,那么,以a为最大,b为最小的序列数目就有 2 i − j − 1 2^{i-j-1} 2ij1
因此,对于情况2,我们可以列出式子(a数组已经排好序): ∑ i = 1 n ∑ j = i + 1 n a i ∗ a j ∗ 2 i − j − 1 = 1 2 ∑ i = 1 n a i 2 i ∑ i = j + 1 n a j 2 − j \sum_{i=1}^n\sum_{j=i+1}^na_i*a_j*2^{i-j-1}={1\over 2}\sum_{i=1}^na_i2^i\sum_{i=j+1}^na_j2^{-j} i=1nj=i+1naiaj2ij1=21i=1nai2ii=j+1naj2j
然后后缀和优化下求值就好了。

const int maxn=2e5+10;
#define int ll
int n,a[maxn];
ll val1[maxn],val2[maxn];
ll sum1[maxn],sum2[maxn];
ll w1[maxn],w2[maxn];
ll inv2=ksm(2,mod-2);
ll solve(){
    sort(a+1,a+1+n);
    ll res=0;
    for(int i=1;i<=n;++i)res=(res+a[i]*a[i]%mod)%mod;
    w1[0]=1;
    for(int i=1;i<=n;++i){
        w1[i]=w1[i-1]*inv2%mod;
        //cerr<<w1[i]<<" ";
        //cerr<<w1[i]*ksm(2,i)%mod<<endl;
    }
    w2[0]=1;
    for(int i=1;i<=n;++i){
        w2[i]=w2[i-1]*2%mod;
        //cerr<<w2[i]<<" ";
    }
    for(int i=1;i<=n;++i){
        val1[i]=a[i]*w1[i]%mod;
    }
    for(int i=1;i<=n;++i){
        val2[i]=a[i]*w2[i]%mod;
    }
    ll p2=0;
    for(int i=1;i<=n;++i)sum1[i]=sum1[i-1]+val1[i],sum1[i]%=mod;
    for(int i=n;i>=1;--i)sum2[i]=sum2[i+1]+val2[i],sum2[i]%=mod;
    for(int i=1;i<n;++i){
        p2=(p2+val1[i]*sum2[i+1]%mod)%mod;
    }
    res=(p2*inv2+res)%mod;
    return res;
}
 
signed main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	#endif
	//clock_t c1 = clock();
	//===========================================================
    read(n);
    rep(i,1,n)read(a[i]);
    cout<<solve()<<endl;
	//===========================================================
	//std::cerr << "Time:" << clock() - c1 << "ms" << std::endl;
	return 0;
}

C.Multiple Sequences

这道题没有写出来,看了别人的证明之后感觉真的妙啊Orz
cf讨论区
首先,这个数列需要满足 a i a_i ai a i − 1 a_{i-1} ai1的倍数,那么,我们其实可以关注到最后一个数a[n]应该是某种意义上的“集大成者”(?),也就是说,它肯定是数列中其他元素的倍数。
接下来的分析来自讨论区:
对于某个确定的 a n a_n an,因为前面的数一定是 a n a_n an的因数,所以,有以下结论:
(1).若 a n = p 1 Q 1 p 2 Q 2 … … p k Q k a_n=p_1^{Q_1}p_2^{Q_2}……p_k^{Q_k} an=p1Q1p2Q2pkQk,对于任意的 a i = p 1 q 1 p 2 q 2 … … p k q k a_i=p_1^{q_1}p_2^{q_2}……p_k^{q_k} ai=p1q1p2q2pkqk,有 Q 1 > = q 1 , Q 2 > = q 2 … … Q k > = q k Q_1>=q_1,Q_2>=q_2……Q_k>=q_k Q1>=q1,Q2>=q2Qk>=qk
(2).若 a n = p 1 Q 1 p 2 Q 2 … … p k Q k a_n=p_1^{Q_1}p_2^{Q_2}……p_k^{Q_k} an=p1Q1p2Q2pkQk,对于其中的某一个质因数p,我们设 y i = q i − q i − 1 y_i=q_i-q_{i-1} yi=qiqi1(其中 q i q_i qi为质数p在i中的次数, q i − 1 q_{i-1} qi1同理),则有
∑ i = 1 n y i = Q \sum_{i=1}^{n}y_i=Q i=1nyi=Q
因此,这个问题就可以看成 ∑ i = 1 n y i = Q \sum_{i=1}^{n}y_i=Q i=1nyi=Q这个关于 y i y_i yi的方程有多少个非负整数解,显然,很经典的问题,就是 C n + Q − 1 n − 1 C_{n+Q-1}^{n-1} Cn+Q1n1(隔板法/生成函数)。
剩下的细节看代码应该就能理解了。

const int maxn=4e5+10;
const int mod=998244353;
#define int ll
bool vis[maxn];
int pri[maxn],tot;
ll f[maxn],finv[maxn];
ll ksm(ll a,ll n){
	ll res=1;
	while(n){
		if(n&1)res=res*a%mod;
		a=a*a%mod;
		n>>=1;
	}
	return res;
}
void init(){
	f[0]=1;
	for(int i=1;i<maxn;++i)f[i]=f[i-1]*i%mod;
	finv[maxn-1]=ksm(f[maxn-1],mod-2);
	for(int i=maxn-2;i>=0;--i){
		finv[i]=finv[i+1]*(i+1)%mod;
	}
	for(int i=2;i<maxn;++i){
		if(!vis[i])pri[++tot]=i;
		for(int j=1;j<=tot&&i*pri[j]<maxn;++j){
			vis[i*pri[j]]=1;
			if(i%pri[j]==0)break;
		}
	}
}
 
ll C(ll n,ll m){
	return f[n]*finv[n-m]%mod*finv[m]%mod;
}
 
ll n,m;
 
signed main(){
	//freopen("in.txt","r",stdin);
	init();
	ll ans=0;
	read(n),read(m);
	for(int i=1;i<=m;++i){
		int now=i;
		ll res=1;
		for(int j=1;j<=tot&&pri[j]*pri[j]<=now;++j){
			if(now%pri[j]==0){
				int cnt=0;
				while(now%pri[j]==0){
					++cnt;
					now/=pri[j];
				}
				res=res*C(n+cnt-1,n-1)%mod;
				res%=mod;
			}
		}
		if(now>1)res=res*C(n,n-1)%mod,res%=mod;
		ans=(ans+res)%mod;
	}
	cout<<ans<<endl;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值