Codeforces Round #772 (Div. 2) A B C D

本文介绍了Codeforces Round #772 (Div.2)的四道编程题目,包括A. MinOrSum、B. MEX and Array、C. Differential Sorting和D. Infinite Set。针对每道题目,解析了思路并提供了C++代码实现,涉及二进制操作、数组处理、区间维护和动态规划等算法技巧。
摘要由CSDN通过智能技术生成

Codeforces Round #772 (Div. 2) A B C D

A. Min Or Sum

思路: 一眼题,最好的情况就是二进制形式下出现过的数字最多保留 1 1 1位。全部 ∣ | 起来即可。

  • 参考代码:
#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
#define int long long 
#define re register int
#define pb emplace_back
#define lowbit(x) (x&-x)
#define fer(i,a,b) for(re i = a ; i <= b ; i ++)
#define der(i,a,b) for(re i = a ; i >= b ; i --)
#define snow ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int lcm(int a,int b){return a*b/gcd(a,b);}
typedef pair<int,int>PII;
typedef pair<int,string>PIS;
signed main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        int k=0;
        for(int i=1;i<=n;i++){
            int x;
            cin>>x;
            k|=x;
        }
        cout<<k<<endl;
    }
    return 0;
}

B. MEX and Array

思路: 贪心,从左到右扫描,如果碰到 L o c a l Local Local M a x i m u m s Maximums Maximums,那么改变右边的数使 a [ i + 1 ] a[i+1] a[i+1]变成 m a x ( a [ i ] , a [ i + 2 ] ) max(a[i],a[i+2]) max(a[i],a[i+2])。为什么取 m a x max max呢,因为如果 a [ i + 2 ] a[i+2] a[i+2] a [ i ] a[i] a[i]大,那么我们显然将 a [ i + 1 ] a[i+1] a[i+1]变成a[i+2]更好,因为不但满足使得左边的 L o c a l Local Local M a x i m u m s Maximums Maximums消失,并且确保右边的 a [ i + 2 ] a[i+2] a[i+2]一定无法构成 L o c a l Local Local M a x i m u m s Maximums Maximums,即是 a [ i + 3 ] a[i+3] a[i+3] a [ i + 2 ] a[i+2] a[i+2]小。反之如果 a [ i + 1 ] a[i+1] a[i+1]变成 a [ i ] a[i] a[i]那么左边确实保证了 L o c a l Local Local M a x i m u m s Maximums Maximums消失,但是如果 a [ i + 3 ] a[i+3] a[i+3] a [ i + 2 ] a[i+2] a[i+2]小,我们又要浪费一次机会去放大 a [ i + 3 ] a[i+3] a[i+3],显然前者更优。

  • 参考代码:
#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
#define int long long 
#define re register int
#define pb emplace_back
#define lowbit(x) (x&-x)
#define fer(i,a,b) for(re i = a ; i <= b ; i ++)
#define der(i,a,b) for(re i = a ; i >= b ; i --)
#define snow ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int lcm(int a,int b){return a*b/gcd(a,b);}
typedef pair<int,int>PII;
typedef pair<int,string>PIS;
const int N=2e5+10;
int a[N];
signed main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)cin>>a[i];
        int ans=0;
        for(int i=2;i<=n-1;i++){
            if(a[i]>a[i-1]&&a[i]>a[i+1]){
                a[i+1]=max(a[i],a[i+2]);
                ans++;
            }
        }
        cout<<ans<<endl;
        for(int i=1;i<=n;i++)cout<<a[i]<<' ';
        cout<<endl;
    }
    return 0;
}

C. Differential Sorting

思路: 比赛时候 C C C翻水水了,歪掉了。以为区间维护,一开始写了 3 3 3 s t r u c t struct struct维护后缀区间最值。后来 w a wa wa掉了。 正解:从性质出发:1.如果 a [ n − 1 ] > a [ n ] a[n-1]>a[n] a[n1]>a[n]必错,因为 a [ n − 1 ] a[n-1] a[n1]无法成为一个小于等于 a [ n ] a[n] a[n]的数。2:如果 a [ n ] a[n] a[n]是正数,那么在满足性质 1 1 1情况下必然可以通过 n − 2 n-2 n2次构造使得前 n − 2 n-2 n2个数全部变成 a [ n − 1 ] − a [ n ] a[n-1]-a[n] a[n1]a[n]。3:如果 a [ n ] a[n] a[n]是负数,前面出现的数必然都是负数才行,一旦出现 a [ i ] > a [ i + 1 ] a[i]>a[i+1] a[i]>a[i+1]的情况,那么必然无法使得 a [ i ] < = a [ i + 1 ] a[i]<=a[i+1] a[i]<=a[i+1],因为小的负数-大的负数为大于等于 0 0 0的整数,也必然 > a [ i + 1 ] >a[i+1] >a[i+1],所以一旦有 a [ n ] a[n] a[n]是负数,除非原数组本身是非降序,操作次数为 0 0 0,否则一定是 − 1 -1 1

  • 参考代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int a[N];
int main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)cin>>a[i];
        int x=a[n-1]-a[n];
        if(x>0){
            cout<<"-1"<<endl;
            continue;
        }
        if(a[n]<0){
            bool ok=true;
            for(int i=1;i<=n-1;i++){
                if(a[i]>a[i+1])ok=false;
            }
            if(ok){
                cout<<0<<endl;
                continue;
            }
            cout<<"-1"<<endl;
        }
        else{
            cout<<n-2<<endl;
            for(int i=1;i<=n-2;i++){
                cout<<i<<' '<<n-1<<' '<<n<<endl;
            }
        }
    }
    return 0;
}

D. Infinite Set

思路: 通过题目可知除了本身在 a a a数组中出现过的数在 S S S中,依次迭代的 2 ∗ a [ i ] + 1 2*a[i]+1 2a[i]+1 4 ∗ a [ i ] 4*a[i] 4a[i]也会依次出现在容器中。那么必定有 k < = f ( x ) < k + 1 k<=f(x)<k+1 k<=f(x)<k+1 k k k k + 1 k+1 k+1 n n n在__ l o g ( ) log() log()下的左右边界。例如 2 2 < = f ( 5 ) < 2 3 2^2<=f(5)<2^3 22<=f(5)<23,那么 k 为 2 k为2 k2 k + 1 k+1 k+1 3 3 3,那么显然 f ( x ∗ 2 + 1 ) f(x*2+1) f(x2+1)的左边界为 k + 1 k+1 k+1 f ( x ∗ 4 ) f(x*4) f(x4)的左边界为 k + 2 k+2 k+2。那么我们很容易发现一个 d p dp dp转移方程,左边界为 k + 2 k+2 k+2的方案数除了本身以外还可以从 k k k k + 1 k+1 k+1处转移过来,分别通过方程 f ( x ∗ 4 ) f(x*4) f(x4) f ( x ∗ 2 + 1 ) f(x*2+1) f(x2+1)。并且在方程转移开始之前我们需要删掉无用的在 a a a数组中重复出现的数,避免一个相同的数被多次重复计算。例如 a [ 1 ] = 5 , a [ 2 ] = 10 a[1]=5,a[2]=10 a[1]=5a[2]=10 a [ 2 ] a[2] a[2]可以通过 a [ 1 ] a[1] a[1]获得,所以 a [ 2 ] a[2] a[2]我们没必要继续保留。那么这个操作的实现只需将数组从小到大排序,如果将一个数回溯的过程遇到了之前标记过的数说明这个数可以通过前面标记过的数获得而来,那么没必要继续保存。例如: 35 35 35回溯过程是 35 35 35 17 17 17 8 8 8 2 2 2。如果前面这个数出现任意,那么 35 35 35即可被转移过来。那么接下去我们就可以进行 d p dp dp转移,统计最终的总数小于 2 p 2^p 2p的个数。还有一点,为什么进行 d p dp dp转移过程中不会出现同样的数呢?因为我们前面已经将所有会被任意小数转移过来的大数全部删除,那么剩余留在数组中的数必然全是互相不可转移。并且转移路线唯一,什么意思呢?比如上面提到的 35 35 35,回溯路径是唯一的,假使后期转移过程出现相同的数,那么说明这个数不断回溯到原数组中会出现 a [ j ] a[j] a[j]可以被 a [ i ] a[i] a[i]转移而来的情况,那么显然与我们已经删掉所有干扰数相矛盾。所以转移方案数中的数都是独一无二的

  • 参考代码:
#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
#define int long long 
#define re register int
#define pb emplace_back
#define lowbit(x) (x&-x)
#define fer(i,a,b) for(re i = a ; i <= b ; i ++)
#define der(i,a,b) for(re i = a ; i >= b ; i --)
#define snow ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int lcm(int a,int b){return a*b/gcd(a,b);}
typedef pair<int,int>PII;
typedef pair<int,string>PIS;
const int N=2e5+10;
int a[N];
const int mod=1e9+7;
signed main(){
    int n,p;
    cin>>n>>p;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    unordered_map<int,int>hash;
    sort(a+1,a+1+n);
    vector<int>S;
    for(int i=1;i<=n;i++){
        int j=a[i];
        bool ok=true;
        while(j){
            if(hash[j]==1)ok=false;
            if(j&1)j>>=1;
            else if(j&3)break;
            else j>>=2;
        }
        if(ok){//如果无法转移过来那么放进S中
            hash[a[i]]=1;
            S.push_back(a[i]);
        }
    }
    vector<int>sum(30),dp(p);
    for(auto x:S){
        sum[__lg(x)]++;
    }
    int ans=0;
    for(int i=0;i<p;i++){
        if(i<30){
            dp[i]=sum[i];
        }
        if(i>=1){
            dp[i]=(dp[i]+dp[i-1])%mod;
        }
        if(i>=2){
            dp[i]=(dp[i]+dp[i-2])%mod;
        }
        ans=(ans+dp[i])%mod;
    }
    cout<<ans<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值