Codeforces Round #737 (Div. 2) 题解(A-C)

Codeforces Round #737 (Div. 2) 题解(A-C)

A. Ezzat and Two Subsequences

题目大意:

给定 n n n个整数,将这 n n n个整数分成两组,使得两组整数的平均数之和最大,输出这个最大的平均数之和。

解题思路:

通过观察样例可以发现,只要将最大的整数单独作为一组就行了。

那么为什么这样是对的呢,可以考虑进行这样分组之和,从 n − 1 n-1 n1个整数那组拿任意一个整数过来能否取得更优的解,列个式子推一推就能明白了,因为本人比较懒就不写详细的证明过程了。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int a[N];
int n;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        sort(a+1,a+1+n);
        double sum=0;
        for(int i=1;i<n;i++) sum+=a[i];
        sum/=(n-1);
        printf("%.8f\n",sum+a[n]);
    }
    return 0;
}

B. Moamen and k-subarrays

题目大意:

给出 n n n个不同的整数,问是否能将这 n n n个整数分成 k k k块连续的子数组之后,通过移位组合成一个升序的数组。

解题思路:

因为分块过后,子数组内部的顺序是不会发生改变的,而我们最后要得到的数组一定是按照严格的顺序排好的。

所以只要我们将整个数组按照升序排序,得到每个数组在升序数组中的位置。

a i a_i ai在最终数组的下标是 b i b_i bi,那么当且仅当 b i = b i − 1 + 1 b_i=b_{i-1}+1 bi=bi1+1的情况下 a i 和 a i − 1 a_i和a_{i-1} aiai1才能分到同一块中。

相对的,如果 b i ≠ b i − 1 + 1 b_i\neq b_{i-1}+1 bi=bi1+1,那么 b i b_i bi b i − 1 b_{i-1} bi1一定要被分成两块。

要注意的一点是分成一块是没有意义的,所以我们要将答案初始化为 1 1 1

时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int a[N],b[N];
int n,k;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&k);
        vector<int> v;
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
            v.push_back(a[i]);
        }
        sort(v.begin(),v.end());
        for(int i=0;i<n;i++) b[i]=lower_bound(v.begin(),v.end(),a[i])-v.begin();
        int res=1;
        for(int i=1;i<n;i++){
            if(b[i]!=b[i-1]+1) res++;
        }
        puts(k>=res?"YES":"NO");
    }
    return 0;
}

C. Moamen and XOR

题目大意:

构造一个 n n n个整数的数组 a a a,其中每个整数都要小于 2 k 2^k 2k

问有多少种方案能够使得以下条件成立:

a 1 & a 2 & a 3 & . . . & a n ≥ a 1 ⊕ a 2 ⊕ a 3 ⊕ . . . ⊕ a n a_1\And a_2 \And a_3\And...\And a_n\ge a_1\oplus a_2 \oplus a_3\oplus ...\oplus a_n a1&a2&a3&...&ana1a2a3...an

解题思路:

做法有点像数位dp,但其实也可以说是二进制的线性dp。

因为所有整数要小于 2 k 2^k 2k,所以对于每个整数在二进制的表示下最多只有 k k k位(如果 k = 0 k=0 k=0的话,那所有数都等于1,答案也就是1)。

所以我们从高位向低位考虑,考虑每一位的所有情况:

  • 如果当前位 & \And & ⊕ \oplus 的结果分别是 1 1 1 1 1 1,那么只有所有数这一位都为 1 1 1,并且n是奇数,一共只有 1 1 1种情况。
  • 如果当前位 & \And & ⊕ \oplus 的结果分别是 1 1 1 0 0 0,那么只有所有数这一位都为1,并且n是偶数,一共只有 1 1 1种情况。
  • 如果当前位 & \And & ⊕ \oplus 的结果分别是 0 0 0 1 1 1,那么只有奇数个数的这一位为1,并且不能全部的数这一位都为1。
    方案数: { 2 n − 1 − 1 n % 2 = 1 2 n − 1   n % 2 = 0 \begin{cases}2^{n-1}-1&n\%2=1 \\ 2^{n-1} \ &n\%2=0\end{cases} {2n112n1 n%2=1n%2=0
  • 如果当前位 & \And & ⊕ \oplus 的结果分别是 0 0 0 0 0 0,那么只有偶数个数的这一位为1,并且不能全部的数这一位都为1。
    方案数: { 2 n − 1 n % 2 = 1 2 n − 1 − 1   n % 2 = 0 \begin{cases}2^{n-1}&n\%2=1 \\ 2^{n-1}-1 \ &n\%2=0\end{cases} {2n12n11 n%2=1n%2=0

可能会有人不知道 2 n − 1 2^{n-1} 2n1怎么来的,众所周知组合数的和 ∑ n = 0 m C m n = 2 m \sum\limits_{n=0}^m C_m^n=2^m n=0mCmn=2m,其中奇数项和偶数项的和都等于 2 m − 1 2^{m-1} 2m1

f [ i ] [ 0 ] f[i][0] f[i][0]为仅考虑最高的 i i i位使得 & \And & ⊕ \oplus 的结果相等的方案数, f [ i ] [ 1 ] f[i][1] f[i][1]为仅考虑最高的 i i i位使得 & \And &的结果大于 ⊕ \oplus 的方案数。

那么可以通过上述分析得到状态转移方程:

  • f [ i ] [ 0 ] = { f [ i − 1 ] [ 0 ] × ( 2 n − 1 + 1 ) n % 2 = 1 f [ i − 1 ] [ 0 ] × ( 2 n − 1 − 1 ) n % 2 = 0 f[i][0]=\begin{cases}f[i-1][0]\times(2^{n-1}+1)&n\%2=1\\f[i-1][0]\times(2^{n-1}-1) &n\%2=0\end{cases} f[i][0]={f[i1][0]×(2n1+1)f[i1][0]×(2n11)n%2=1n%2=0(就是在前一位相等的基础上分别添上00和11, & \And & ⊕ \oplus 仍然相等)
  • f [ i ] [ 1 ] = { f [ i − 1 ] [ 1 ] × 2 n n % 2 = 1 f [ i − 1 ] [ 0 ] + f [ i − 1 ] [ 1 ] × 2 n n % 2 = 0 f[i][1]=\begin{cases}f[i-1][1]\times2^n&n\%2=1\\ f[i-1][0]+f[i-1][1]\times2^n&n\%2=0\end{cases} f[i][1]={f[i1][1]×2nf[i1][0]+f[i1][1]×2nn%2=1n%2=0
    (如果在上一位已经使得 & \And &大于 ⊕ \oplus ,那么在当前位对于任何一种情况都是 & \And &大于 ⊕ \oplus 。如果 n n n是偶数的话,会有一种所有整数在当前二进制位都为1使得 & \And & ⊕ \oplus 的结果是1和 0 0 0的情况,这种可以使得上一位的相等状态转移到当前的大于状态)

边界为 f [ 0 ] [ 0 ] = 1 f[0][0]=1 f[0][0]=1

计算 2 n 2^n 2n用快速幂就行了,在容易溢出的地方别忘了取模。

时间复杂度 O ( l o g n + k ) O(logn+k) O(logn+k)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2e5+10,mod=1e9+7;
int n,k;
LL f[N][2];
int qmi(int a,int b){
    int res=1;
    while(b){
        if(b&1) res=(LL)res*a%mod;
        a=(LL)a*a%mod;
        b>>=1;
    }
    return res;
}
int main()
{
    f[0][0]=1;
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&k);
        if(!k){
            puts("1");
        }
        else{
             LL p=qmi(2,n-1);
             for(int i=1;i<=k;i++){
                if(n%2){
                    f[i][0]=(f[i-1][0]*(p+1))%mod;
                    f[i][1]=(f[i-1][1]*p*2%mod)%mod;
                }
                else{
                    f[i][0]=(f[i-1][0]*(p-1))%mod;
                    f[i][1]=(f[i-1][0]+f[i-1][1]*p*2)%mod;
                }
             }
             printf("%d\n",(f[k][0]+f[k][1])%mod);
        }
    }
    return 0;
}
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值