Codeforces Round #773 (Div. 2)简训

本文针对 Codeforces Round #773 (Div.2) 的五道题目进行了详细的解析,包括 Hard Way、Power Walking、Great Sequence、Repetitions Decoding 和 Anonymity Is Important,分享了解题思路与实现代码。
摘要由CSDN通过智能技术生成

导语

稳定三个题,就是第一个题抽风错了很多次,以及队友剩4min痛失第四题

涉及的知识点

STL,思维,字符串

链接:Codeforces Round #773 (Div. 2)

题目

A Hard Way

题目大意:略

思路:一开始做只觉得有边平行x轴就行了,但是还要另一个点在平行边下方

代码

#include <bits/stdc++.h>
#define int long long
const int inf=0x3f3f3f3f;
const int maxn=2e6+5;
using namespace std;
int t,x[3],y[3];
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >>t;
    while(t--) {
        for(int i=0; i<3; i++)
            cin >>x[i]>>y[i];
        int res=0;
        for(int i=0; i<3; i++)
            if(y[i]==y[(i+1)%3]) {//平行线
                if(y[(i+2)%3]<y[i]) {//另一个在平行线下面
                    res=abs(x[i]-x[(i+1)%3]);
                    break;
                }
            }
        cout <<res<<endl;
    }
    return 0;
}

B Power Walking

题目大意:略

思路:很容易就可以得出结论,首先如果人数小于数字种类,那么一定能使得总和为数字种类,否则就会有新的一个人只拿走一个数字(每个人至少一个),增加总和

代码

#include <bits/stdc++.h>
#define int long long
const int inf=0x3f3f3f3f;
const int maxn=3e5+5;
using namespace std;
int t,n,a[maxn];
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >>t;
    while(t--) {
        cin >>n;
        for(int i=1; i<=n; i++)cin >>a[i];
        sort(a+1,a+1+n);//排序,方便去重
        int len=unique(a+1,a+1+n)-a-1;
        for(int i=1; i<=n; i++)
        //如果人数超过种类,那么一定会有新的人只拿一个
            i>len?cout <<++len<<" ":cout <<len<<" ";
        cout <<endl;
    }
    return 0;
}

C Great Sequence

题目大意:给出长度为n的序列和一个正整数x,判断能否将序列分成多对数,每对数的第一个数乘x等于第二个数,如果不能则判断至少加几个数能使得序列满足条件

思路:题目实际上是一个配对问题,贪心的去考虑,将序列排序,然后从小到大的进行配对,如果没有剩下的就代表需要插入新的数字

代码

#include <bits/stdc++.h>
#define int long long
const int inf=0x3f3f3f3f;
const int maxn=3e5+5;
using namespace std;
int t,n,a[maxn],x;
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >>t;
    while(t--) {
        cin >>n>>x;
        map<int,int>use;//统计个数用于配对
        for(int i=1; i<=n; i++)cin >>a[i],use[a[i]]++;
        int ans=0;
        sort(a+1,a+1+n);//必须排序
        for(int i=1; i<=n; i++) {
            int p=a[i];
            if(use[p]==0)continue;//已经用完了
            if(use[p*x])
                use[p*x]--;
            else
                ans++;
            use[p]--;//
        }
        cout <<ans<<endl;
    }
    return 0;
}

D Repetitions Decoding

题目大意:给出一共正整数序列,定义串联重复和相关操作:

  1. 一个串联重复的序列x长度必须是偶数2k,对前k个元素有 x i = x i + k x_i=x_{i+k} xi=xi+k
  2. 一个序列a可以被分成多个串联重复每个子段也是串联重复
  3. 每一次操作可以选择一个数字c然后向已有的序列中的任意位置插入连续两个c

现在判断给定的初始序列能否切割成多个串联重复(在可以操作的前提下),如果能,输出所有操作以及切分出来的串联重复子串长度

思路:如果存在数字出现的次数为奇数,那必然无解
现在来探讨一下如何构造串联重复,利用贪心的思路,每一步构造前面的串,构造完后集体删去,这样的话就可以不用考虑前面的影响而继续获得后面的串,以 [ 1 , 3 , 1 , 2 , 2 , 3 ] [1,3,1,2,2,3] [1,3,1,2,2,3]为例,第一个元素为1,那么找到下一个1,令两者匹配,单独拆出来,也就是 [ 1 , 3 , 1 ] [1,3,1] [1,3,1],想要其串联重复,需要在后一个1后面插3,于是得到 [ 1 , 3 , 1 , 3 , 3 ] [1,3,1,3,3] [1,3,1,3,3],将符合条件的去掉,与剩余的组合得到 [ 3 , 2 , 2 , 3 ] [3,2,2,3] [3,2,2,3],该串本身就是串联重复,因此构造完成,详见代码

代码

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
#define pii pair<int,int>

using namespace std;
vector<int> vi;
void ins(int p,int c) {
    vi.insert(vi.begin()+p,c);
    vi.insert(vi.begin()+p,c);
}
void solve() {
    vi.clear();
    map<int,bool> mii;
    vector<pii> q;//记录每次的操作
    vector<int> d;//记录每次操作后得到的长度
    int n;
    scanf("%d",&n);
    for(int i = 1; i<=n; i++) {
        int x;
        scanf("%d",&x);
        vi.push_back(x);
        mii[x]^=1;//记录是否为偶数
    }
    map<int,bool>::iterator it=mii.begin();
    for(; it!=mii.end(); it++)//判断是否有奇数
        if(it->second) {//存在奇数就无解
            printf("-1\n");
            return ;
        }
    int i = 0,p=0;
    while(i < n) {
        for(int j = i+1; j<n; j++) {
            if(vi[j]==vi[i]) {//找到两个数字相同
                p=j;
                for(int k = 1; k < j-i; k++) {
                    if(j+k>=n||vi[i+k]!=vi[j+k]) {
                        //j+k>=n是防止因为越界而不能复制
                        //第二个条件是判断如果不能就复制
                        ins(j+k,vi[i+k]);//按照题设条件复制
                        n+=2;
                        q.push_back(make_pair(j+k,vi[i+k]));
                    }
                }
                d.push_back((p-i)*2);//相当于把p~i复制了一遍
                break;
            }
        }
        i+=(p-i)*2;
    }
    cout << q.size() << endl;
    for(i = 0; i < q.size(); i++)
        cout << q[i].first << " " << q[i].second << endl;
    cout << d.size() << endl;
    for(i = 0; i < d.size(); i++) {
        cout << d[i];
        if(i<d.size()-1)
            cout << " ";
        else
            cout << endl;
    }
}

int main() {
    int t = 1;
    scanf("%d",&t);
    while(t--)
        solve();
    return 0;
}

E Anonymity Is Important

题目大意:有一个01序列,但是不清楚哪些是0哪些是1,现在有以下3中操作

  1. l l l r r r 0 0 0:代表区间 [ l , r ] [l,r] [l,r]全是0
  2. l l l r r r 1 1 1:代表区间 [ l , r ] [l,r] [l,r]至少有一个1
  3. q q q i n d e x index index:询问下标为 i n d e x index index的元素是0还是1

输出每次询问的结果(不保证元素的值一定能确定下来)

思路:只有两种方法确定一个元素的值:直接区间全是0或者对于一个包含该元素的区间,其余元素都已确定,而且该区间至少有一个1,那么,不断缩小带1区间就可以更快的得到哪个元素是1,因此可以维护两个信息:未确定元素和带1区间
未确定元素在确定之后需要被删除,使用set维护可以二分查找删除,带1区间的维护使用map,键为左端点,值为右端点,比较相邻区间的右端点可以区间嵌套

代码

#include <bits/stdc++.h>
#define int long long
#define INF 0x3f3f3f3f
using namespace std;
int n,q;
set<int>s;
map<int,int>seq;
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >>n>>q;
    for(int i=0; i<=n+1; i++)s.insert(i);
    while(q--) {
        int t;
        cin >>t;
        if(!t) {
            int l,r,x;
            cin >>l>>r>>x;
            if(x) {//如果区间内至少存在一个
                auto i=seq.lower_bound(l);
                //找到区间左端点与l相等或更大的,查看是否被l,r覆盖
                if(i!=seq.end()&&i->second<=r)continue;
                //如果覆盖了,那么跳过即可,因为区间已经更小了
                seq[l]=r;//否则将左端点为l的区间缩小
                i=seq.find(l);//查找左端点为l的位置
                while(i!=seq.begin()&&r<=prev(i)->second)seq.erase(prev(i));
                //如果l的前一个区间包含了l,r,删掉前面一个,因为区间已经缩小了
            } else
                while(1) {//删除l,r之间的,代表一定为0
                    auto i=s.lower_bound(l);//二分查找比l大的值
                    if(*i>r)break;
                    s.erase(i);
                }
        } else {
            int x;
            cin >>x;
            if(!s.count(x))cout <<"NO\n";//如果不在,代表已经被排除,也就一定是0
            else {//到这里来代表x的情况不确定
                int l=*prev(s.find(x)),r=*next(s.find(x));
                //找到其所属的最小不确定区间
                auto i=seq.upper_bound(l);//查找被l刚好覆盖的左端点
                if(i!=seq.end()&&i->first<=x&&i->second<r)cout <<"YES\n";
                //x在这个区间里,而这个区间里只有x没有确定下来(未确定的l和r不在这个区间内)
                else cout <<"N/A\n";
            }
        }
    }
    return 0;
}

参考文献

  1. codeforces 1641C Anonymity Is Important (思维好题,STL)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值