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

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

A.The Third Three Number Problem

题意: 判断是否有 3 3 3 个数 a , b , c a,b,c a,b,c 使得 a a a ^ b b b + b b b ^ c c c + c c c ^ a = n a = n a=n

思路: 首先看样例,发现 n = 1 n=1 n=1 的情况下答案为 − 1 -1 1 ,其余情况 n n n 皆为偶数且有值 , 猜想 n n n 为奇数情况答案为 − 1 -1 1 。证明: 3 3 3 个数 a , b , c a,b,c a,b,c 二进制形式下最后一位的取值有 000 000 000 001 001 001 011 011 011 111 111 111 四种情况,可以发现任意一种情况都不能构成 奇数和 ,所以如果 n n n是奇数,那么值必然为 − 1 -1 1。如果是偶数我们直接构造 0 0 0 0 0 0 n / 2 n/2 n/2 即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
//Edit Author: Snow
void cf(){
    int n;
    cin>>n;
    if(n%2){
        cout<<"-1"<<endl;
    }
    else{
        cout<<0<<' '<<0<<' '<<n/2<<endl;
    }
} 
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int t=1;
    cin>>t;
    while(t--){
        cf();
    }
}

B.Almost Ternary Matrix

题意: 给你一个 n ∗ m n * m nm 的矩阵且 n n n m m m 都是 偶数 ,问如果构造一个 01 01 01 矩阵,使得每个元素有且仅有两个对立元素存在。( 1 1 1的对立是 0 0 0 0 0 0的对立是 1 1 1)。

思路: 面向样例编程题,通过 例 1 1 1 样例结合 例 3 3 3 样例很容易发现一种构造方法。如 例 3 3 3 如果将其构造成如下:

  • 1 0 0 1
    0 1 1 0
    0 1 1 0
    1 0 0 1

那么这道题就一眼得知如何构造了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
//Edit Author: Snow
const int N = 55;
int a[N][N];
void cf(){
    int n,m;
    cin>>n>>m;
    n/=2;
    m/=2;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++){
            if(i%2&&j%2){
                a[i*2+1][j*2+1]=1;
                a[i*2+1][j*2+2]=0;
                a[i*2+2][j*2+1]=0;
                a[i*2+2][j*2+2]=1;
            }
            else if(i%2&&j%2==0){
                a[i*2+1][j*2+1]=0;
                a[i*2+1][j*2+2]=1;
                a[i*2+2][j*2+1]=1;
                a[i*2+2][j*2+2]=0;
            }
            else if(i%2==0&&j%2){
                a[i*2+1][j*2+1]=0;
                a[i*2+1][j*2+2]=1;
                a[i*2+2][j*2+1]=1;
                a[i*2+2][j*2+2]=0;
            }
            else if(i%2==0&&j%2==0){
                a[i*2+1][j*2+1]=1;
                a[i*2+1][j*2+2]=0;
                a[i*2+2][j*2+1]=0;
                a[i*2+2][j*2+2]=1;
            }
        }
    for(int i=1;i<=n*2;i++){
        for(int j=1;j<=m*2;j++)cout<<a[i][j]<<' ';
        cout<<endl;    
    }
        
} 
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int t=1;
    cin>>t;
    while(t--){
        cf();
    }
}

C.The Third Problem

题意: 给定一组 0 0 0 ~ n − 1 n-1 n1 的排列 A A A 。问:能构造多少组排列 B B B ,使得 A A A 相似 B B B 。 相似的定义是对于在 A A A B B B 中任意的 l , r l,r l,r 都有 A . M E X ( l , r ) = B . M E X ( l , r ) A.MEX(l,r)=B.MEX(l,r) A.MEX(l,r)=B.MEX(l,r) ,问一共多多少种可能,答案对 1 e 9 + 7 1e9+7 1e9+7 取模。

思路: 根据 M E X MEX MEX的性质:第一个不存在于区间中的最小非负数。我们由小到大去寻找,首先是 0 0 0 的位置,必然是固定不动,否则 0 0 0 原先在的位置 M E X MEX MEX 就不再为 1 1 1。其次是 1 1 1 1 1 1 的位置也必然不动,否则以 01 01 01 为区间两端的 M E X MEX MEX会造成影响。接下去找 2 2 2 的位置,如果 2 2 2 出现在 01 01 01 区间内,那么发现 2 2 2无论在区间内的哪个位置都是有效位置,所以总方案数乘上剩余有效位置,即 ( r − l + 1 ) − 2 ( 0 和 1 的 位 置 ) (r-l+1)-2(0和1的位置) (rl+1)2(01) 。如果不在 01 01 01 区间内类似 01 01 01 之间的关系, 3 3 3 也必然固定不动,扩展 l , r l,r l,r的区间范围。那么得知正解即为 f o r for for 一遍 0 0 0 ~ n − 1 n-1 n1的位置实时扩展 l , r l,r l,r 区间且更新最终的方案数。

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
//Edit Author: Snow
const int N = 1e5+10;
int a[N];
int idx[N];
const int mod=1e9+7;
void cf(){
    int n;
    cin>>n;
    int l,r;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        idx[a[i]]=i;
        if(a[i]==0)l=r=i;
    }
    int res=1;
    for(int i=1;i<n;i++){
        if(idx[i]<l||idx[i]>r){
            l=min(l,idx[i]);
            r=max(r,idx[i]);
        }
        else{
            res=res*(r-l+1-i)%mod;
        }
    }
    cout<<res<<endl;
} 
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int t=1;
    cin>>t;
    while(t--){
        cf();
    }
}

D.Almost Triple Deletions

题意: 给定一组数组,你可以通过任意次可以为 0 0 0 次操作:

  • 选择两个相邻且不相等的数并删除这两个数

求最终可以得到的长度最大的且元素全部相同的数组长度为多长。

思路: 首先我们从最终情况进行考虑,最终剩余的必然空数组或者是一连串相等的数。那么假设这一连串相等的数是 3 3 3 而原数组中第 1 1 1 个数假如不是 3 3 3 那么意味着我们需要找到从第 1 1 1 位开始的一段 可以删除的区间 使得区间删除之后数组的第 1 1 1 位是 3 3 3。由于复杂度支持 O ( n 2 ) O(n^2) O(n2) ,这给我们一个思路,这题可以先将可删除的区间预处理出来。区间可删除的条件易得有:
1 : 1: 1区间长度为偶数。
2 : 2: 2区间内最多数量的数不超过总长度的一半。
那么我们接下去就可以 O ( 1 ) O(1) O(1) 判断一段区间是否可删除。我们可以预处理位置 1 1 1 - n n n ,如果 0 0 0 - i − 1 i - 1 i1 是可以删除的,那么说明当前位置作为开头是合理的,使 d p [ i ] = 1 dp[i]=1 dp[i]=1
接下去本题就可以转化成一种类 LIS 做法,判断枚举到第 j j j 个位置时,当前位置的数与前面同样出现过的相同数之间进行一个更新,如果前面出现相同数的下标 d p [ i ] > 0 dp[i]>0 dp[i]>0 说明是合理的并且需要同样满足区间 [ i + 1 , j − 1 ] [i+1,j-1] [i+1,j1] 可删除,如果 i i i j j j 相邻需要特判一下,如果都满足那么 d p [ j ] = m a x ( d p [ j ] , d p [ i ] + 1 ) dp[j]=max(dp[j],dp[i]+1) dp[j]=max(dp[j],dp[i]+1) ,复杂度 O ( n 2 ) O(n^2) O(n2)。那么最终的 d p [ n ] dp[n] dp[n] 就是以 s [ i ] s[i] s[i] 中的字符为最终连续串所能获得的最大值。但 s [ n ] s[n] s[n] 不一定为最长连续串的字符。所以我们还需要维护一个 m a ma ma ,如果枚举到 j j j 位置时,区间 [ j + 1 , n ] [j+1,n] [j+1,n] 可删除,那么我们更新一下的 m a ma ma 。最终的 m a ma ma 即为最长连续串。

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
//Edit Author: Snow
const int N = 5010;
int a[N];
int dp[N];
void cf(){
    int n;
    cin>>n;
    int delete_table[n+1][n+1]={0};
    for(int i=1;i<=n;i++)cin>>a[i];
    //预处理删除表
    for(int i=1;i<=n;i++){
        int cnt[n+1]={0};
        int ma=0;
        for(int j=i;j<=n;j++){
            cnt[a[j]]++;
            ma=max(ma,cnt[a[j]]);
            if(ma*2<=j-i+1&&(j-i+1)%2==0){
                delete_table[i][j]=1;
            }
        }
    }
    memset(dp,0,sizeof dp);
    dp[1]=1;
    for(int i=2;i<=n;i++){
        if(delete_table[1][i-1])dp[i]=1;
    }
    int ma=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<i;j++){
            if(j+1==i&&a[i]==a[j]&&dp[j]){
                dp[i]=dp[j]+1;
            }
            else{
                if(a[j]==a[i]&&delete_table[j+1][i-1]==1&&dp[j]){
                    dp[i]=max(dp[i],dp[j]+1);
                }
            }
        }
        if(i==n)ma=max(ma,dp[i]);
        else if(delete_table[i+1][n])ma=max(ma,dp[i]);
    }
    cout<<ma<<endl;
} 
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int t=1;
    cin>>t;
    while(t--){
        cf();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值