Codeforces Round #803 (Div. 2)

本文通过分析Codeforces Round #803 (Div.2) 中的四道编程题目,探讨了不同的解题策略。A题通过观察XOR性质,发现所有数的XOR为0的条件。B题指出当操作次数k为1时,可以找到最大增加数量的数。C题通过排除无限循环的可能,确定了满足3SUM封闭性的数列组合。D题提出了一种交互式问题的二分查找解决方案,重点在于理解交换规则。
摘要由CSDN通过智能技术生成

Codeforces Round #803 (Div. 2)https://codeforces.com/contest/1698

A. XOR Mixup

        如果能把n-1的值xor变成最后一个值,那么所有的值xor一下=0,而相等的数xor=0,也就是说所有的数都能当最后一个数

        

#include<bits/stdc++.h>
using namespace std;

#define endl '\n';
#define DE printf("!");
const int maxn = 1e6+10;
int num[maxn];
int qian[maxn];

int main(){
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        for(int i = 1;i<=n;i++)
            cin>>num[i];
        cout<<num[n]<<endl;
    }
}
//直接模拟试试
//只要后面的数能满足被m分解的条件,就可能后面的数分解后再和前面的数相合成

B. Rising Sand

k=1的时候,很明显,从2-n-1挑选间隔为1的数进行增大就行,k>=2的时候,不管怎么操作都会影响至少一个相邻的数,所以k>=2的时候不操作得到的答案就是最多的

#include<bits/stdc++.h>
using namespace std;

#define endl '\n';
#define DE printf("!");
const int maxn = 1e6+10;
int num[maxn];
int qian[maxn];

int main(){
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int t;
    cin>>t;
    while(t--){
        int n,k;
        cin>>n>>k;
        for(int i = 1;i<=n;i++)
            cin>>num[i];
        if(k==1){
            cout<<(n-2)/2+n%2<<endl;
        }
        else{
            int ans = 0;
            for(int i = 2;i<n;i++){
                if(num[i]>num[i-1]+num[i+1])
                    ans++;
            }
            cout<<ans<<endl;
        }
    }
}
//k=1的时候,间隔有最大
//k>=2的时候,想增大某个数必然同时增大相邻的某个数

C. 3SUM Closure

从构造这些数字的角度反向考虑一下,如果有三个同号的数,就会存在一个无限生成的情况,a1+a2+a3 = a4,然后a4+a2+a3 = a5这样无限生成下去,因此是不可能的。然后两个同号带0也是一样,会无限生成数字。比如1+2+0 = 3,然后3+1+2 = 6.。。这样就无限生成了,因此是不存在的。然后我们考虑几种可能的情况,第一种是全0,第二种是只有一个正数/负数和其他0,第三种是一正一负但是一定要和为0,然后带上其他0,第四种是不带0的时候两正一负/两负一正/两负两正,第四种情况因为不带0,所以n是3/3/4这样,直接暴力判断就好了。然后第一种第二种第三种统计一下正数负数个数和总和判断即可

#include<bits/stdc++.h>
using namespace std;

#define endl '\n';
#define DE printf("!");
const int maxn = 1e6+10;
int num[maxn];
int qian[maxn];

int main(){
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        map<int,int>m;
        for(int i = 1;i<=n;i++)
            cin>>num[i];
        int znum = 0,fnum = 0;
        for(int i = 1;i<=n;i++){
            if(num[i]>0)
                znum++;
            if(num[i]<0)
                fnum++;
            m[num[i]]++;
        }
        int j = 0;
        if(n<=4){
            int jj = 1;
            for(int i = 1;i<=n-2;i++)
                for(int k = i+1;k<=n-1;k++)
                    for(int l = k+1;l<=n;l++){
                        if(!m.count(num[i]+num[k]+num[l])){
                            //cout<<i<<" "<<k<<" "<<l<<endl;
                            jj = 0;
                            break;
                        }
                    }
            //cout<<num[1]+num[2]+num[3]<<" "<<m.count(num[1]+num[2]+num[3])<<" "<<jj;
            if(jj)
                j =1;
        }
        else{
            if(fnum+znum<=1)
                j = 1;
            else if(fnum+znum==2){
                int sum = 0;
                for(int i = 1;i<=n;i++)
                    sum+=num[i];
                if(sum==0)
                    j = 1;
            }
        }
        if(j==1){
            cout<<"YES"<<endl;
        }
        else{
            cout<<"NO"<<endl;
        }
    }
}

//因为有可能会存在无限延伸出去的情况:三个同号的,两个同号并且带0的
//因此成立的只有几种情况:全0,只有一个正数,只有一个负数
//一正一负要相加为0才行
//两正一负不能带0,同时要满足题目条件,两负一正也是
//两正两负特殊讨论

D. Fixed Point Guessing

交互题,一般都是二分重点是搞清楚它的交换情况,不是随意排序数字,而是交换,也就是说:如果下标为i的数据为j,那么下标j的数据一定为i。这样我们就可以推导出贪心原则了,就是如果我们询问l-r的数据中,存在奇数个大小在l-r中的数据,那么答案一定在l-r中。原理是,我们可以想一下,l-r的数交换有两种选择,一种是和l-r中不相邻的数交换,一种是和以外的数交换。如果和l-r内的交换,那么一定会在统计l-r的数据中贡献2次,如果和l-r以外的数交换,那么会贡献0次,而只有不动的那个数才会贡献一次。因此,区间含有不动的那个数的统计个数为奇数个,反之为偶数个

#include<bits/stdc++.h>
using namespace std;


#define DE printf("!");
const int maxn = 1e6+10;
int num[maxn];
int qian[maxn];

int main(){
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int t;
    cin>>t;
    while(t--){
        int n,x;
        cin>>n;
        int l = 1,r = n;
        while(l<r){
            int mid = (l+r)>>1;
            int cnt = 0;
            cout<<"? "<<l<<" "<<mid<<endl;
            for(int i = l;i<=mid;i++){
                cin>>x;
                if(x<=mid&&x>=l)
                    cnt++;
            }
            if(cnt&1)
                r = mid;
            else
                l = mid+1;
        }
        cout<<"! "<<l<<endl;
    }
}

//这种交互题一般都是二分
//注意题目,不是随意交换的,如果我们知道下标1的位置是3,那么下标3的位置一定是1
//因此,在一个区间内,如果存在偶数个在这个区间内的数字,说明都是彼此成对的,比如1-4的下标是3 4 1 2或者 3 6 1 8
//如果存在奇数个在这个区间的数字,那么那个没有交换的一定在那个区间内

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值