Codeforces Round #744 (Div. 3) a-e2

本文介绍了四道编程竞赛题目,涉及字符串操作、数组排序、图形路径分析和数组优化。题目分别为Casimir's String Solitaire、Shifting Sort、Ticks和Productive Meeting。通过贪心策略和循环移位解决排序问题,利用双端队列优化数组操作,分析V字形路径条件,以及计算最大对话次数。
摘要由CSDN通过智能技术生成

A Casimir’s String Solitaire

题目

题意
有个只有A,B,C的字符串 每次删去一个A和B 或者 删去一个B和C 问能否删除完

思路
计算A+C==B 能相等就说明能删除完

代码

#include<cstring>//memset
#include<iostream>//c++
#include<algorithm>//stl
#include<string>
#include<set>
#include<queue>
#include<vector>
#include<cmath>
#include <sstream>
#define N 1005
#define mod 998244353
using namespace std;
typedef long long ll;

int t,n,a,b,c;
string s;

int main()
{
    cin>>t;
    while(t--)
    {
        a=b=c=0;
        cin>>s;
        for(int i=0;i<s.size();i++)
        {
            if(s[i]=='A')
                a++;
            else if(s[i]=='B')
                b++;
            else
                c++;
        }
        if(a+c==b)
            cout<<"YES"<<endl;
        else
            cout<<"NO"<<endl;
    }
    return 0;
}

B Shifting Sort

题目

题意
给一个数组a 你可以指定l,r(1<=l<r<=n)向左循环移位d
如 123 左循环移动2位 变成 312
问用不超过n次循环移位给数组a进行排序

思路
不超过n次 很容易想到基础排序 每次排序给一位数找到合适的位置 所以我们每次找到剩下没排序中最小的数的位置cnt 和它对应位置i记录下来 差值就是向左循环偏移量 整个偏移的数组(i到cnt)其实就是向右循环偏移1位 利用前面一位覆盖后面一位 从后往前覆盖 实现数组偏移操作

代码

#include<cstring>//memset
#include<iostream>//c++
#include<algorithm>//stl
#include<string>
#include<set>
#include<queue>
#include<vector>
#include<cmath>
#include <sstream>
#define N 1005
#define mod 998244353
using namespace std;
typedef long long ll;

int t,n,a[60],l[60],r[60],cnt;
string s;

int main()
{
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i=1; i<=n; i++)
        {
            cin>>a[i];
        }
        int num=1;
        cnt=1;
        while(cnt<n)
        {
            int mmin=a[cnt];
            for(int i=cnt+1; i<=n; i++)
            {
                if(a[i]<mmin)
                {
                    mmin=a[i];
                    r[num]=i;
                }
            }
            if(a[cnt]!=mmin)
            {
                l[num]=cnt;
                for(int i=r[num]; i>cnt; i--)
                {
                    a[i]=a[i-1];
                }
                num++;
            }
            a[cnt]=mmin;
            cnt++;
        }
        num--;
        cout<<num<<endl;
        for(int i=1; i<=num; i++)
        {
            cout<<l[i]<<" "<<r[i]<<" "<<r[i]-l[i]<<endl;
        }
    }
    return 0;
}

C Ticks

题目

题意
在nxm方格上有一些标记点 问这些标记点是否都能组成左边和右边都至少为k长度(不包括最下面的顶点)的v字型

思路
暴力每一个标记点 把它当作v最下面的顶点向两端延申并记录两端的长度cnt1,cnt2 如果两端的长度最小的都不少于k 则用vis数组把这个v的点标记下来 最后如果方格里面标记的点在vis中未标记 说明这个点无法构成符合条件的v

代码

#include<cstring>//memset
#include<iostream>//c++
#include<algorithm>//stl
#include<string>
#include<set>
#include<queue>
#include<vector>
#include<cmath>
#include <sstream>
#define N 1005
#define mod 998244353
using namespace std;
typedef long long ll;

int t,n,te,tt,num,m,k;
char a[25][25];
int vis[25][25];

int main()
{
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--)
    {
        memset(vis,0,sizeof(vis));
        cin>>n>>m>>k;;
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=m; j++)
            {
                cin>>a[i][j];
            }
        }
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=m; j++)
            {
                if(a[i][j]=='*')
                {
                    int x1=i-1,y1=j-1,cnt1=0;
                    while((x1>0)&&(y1>0)&&a[x1][y1]=='*')
                    {
                        cnt1++;
                        x1-=1;
                        y1-=1;
                    }
                    
                    int x2=i-1,y2=j+1,cnt2=0;
                    while((x2>0)&&(y2<=m)&&a[x2][y2]=='*')
                    {
                        cnt2++;
                        x2-=1;
                        y2+=1;
                    }
                    
                    int te=min(cnt1,cnt2);
                    if(te>=k)
                    {
                        int t1=i,t2=j;
                        while(t1>=i-te&&t2>=j-te)
                        {
                            vis[t1][t2]=1;
                            t1--;
                            t2--;
                        }
                        t1=i;t2=j;
                        while(t1>=i-te&&t2<=j+te)
                        {
                            vis[t1][t2]=1;
                            t1--;
                            t2++;
                        }
                    }
                }
            }
        }
        int flag=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if(a[i][j]=='*'&&vis[i][j]==0)
                {
                    flag=0;
                    break;
                }
            }
            if(!flag)
                break;
        }
        if(flag)
            cout<<"YES"<<endl;
        else
            cout<<"NO"<<endl;
    }
    return 0;
}

D Productive Meeting

题目

题意
有n个人 每个人都有自己的谈话次数 两个人谈话一次双方都会消耗一次次数
问最多能谈话多少次

思路
贪心 用优先队列每次找的两个剩余谈话次数最多的两个人谈话 不为零的再扔进优先队列里面

代码

#include<bits/stdc++.h>
#define N 200005
#define mod 998244353
using namespace std;
typedef long long ll;

int t,n,te,a[N],b[N];
struct node
{
    int cnt,num;
    node(){};
    node(int cc,int nn)
    {
        cnt=cc;
        num=nn;
    }
    bool operator < (const node &a)const
    {
        return a.num>num;
    }
};

priority_queue<node> qu;

int main()
{
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--)
    {
        while(!qu.empty())
        {
            qu.pop();
        }
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>te;
            if(te<=0)
                continue;
            qu.push(node(i,te));
        }
        node t1,t2;
        int cnnt=1;
        while(qu.size()>1)
        {
            t1=qu.top();
            qu.pop();
            t2=qu.top();
            qu.pop();
            a[cnnt]=t1.cnt;
            b[cnnt]=t2.cnt;
            cnnt++;
            t1.num--;
            t2.num--;
            if(t1.num>0)
            {
                qu.push(t1);
            }
            if(t2.num>0)
            {
                qu.push(t2);
            }
        }
        cout<<--cnnt<<endl;
        for(int i=1;i<=cnnt;i++)
        {
            cout<<a[i]<<" "<<b[i]<<endl;
        }
    }
    return 0;
}

E1 Permutation Minimization by Deque

题目

题意
给一个n个数的数组 1到n每个数字只出现一次 将这些数字按照数组的顺序 自由选择加到双端队列前面或者后面
寻找 字典序最小的双端队列序列

思路
贪心 如果当前数小于队列最前面的数 就加入队列前 否则加入队列后面

代码

#include<cstring>//memset
#include<iostream>//c++
#include<algorithm>//stl
#include<string>
#include<set>
#include<queue>
#include<vector>
#include<cmath>
#include <sstream>
#define N 1005
#define mod 998244353
using namespace std;
typedef long long ll;

int t,n,te,tt;
string s;
deque<int> qu;

int main()
{
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--)
    {
       while(!qu.empty())
        qu.pop_back();
       cin>>n;
       cin>>te;
       qu.push_back(te);
       for(int i=1;i<n;i++)
       {
           cin>>tt;
           if(tt<te)
           {
               qu.push_front(tt);
               te=tt;
           }
           else
           {
               qu.push_back(tt);
           }
       }
       while(!qu.empty())
       {
           cout<<qu.front()<<" ";
           qu.pop_front();
       }
       cout<<endl;
    }
    return 0;
}

E2 Array Optimization by Deque

题目

题意
给定一个数组 按照数组顺序自由选择加入双端队列d前面或者后面 问队列中最小反转次数是多少(当i小于j di大于dj 是为一个反转)

思路
每一步插在前面或者后面 对后续操作贡献的答案是没有影响的(可贪)
找到队列中比当前数字大的数量,队列中比当前数字小的数量,取一个最小值(比它大的数多 放前面 小的数多 放后面)
每增加一个数 都要去寻找当前比它小的数量和当前比它大的数量 单纯不断排序+2分并不能满足时间 特别是当只有一个重复数时 二分时间复杂度趋近o(n)

  • 优化一 去重复(离散化)

一旦去重复再计算比它小的数量就会转变成 区间快速求和 这时 我们就需要线段树或者树状数组来快速求区间和

(借彬宝的线段树模板@彬宝)

代码

#include<cstring>//memset
#include<iostream>//c++
#include<algorithm>//stl
#include<string>
#include<set>
#include<queue>
#include<vector>
#include<cmath>
#include<map>
#include <sstream>
#define N 200005
#define mod 1000000007
using namespace std;
typedef long long ll;

struct node
{
    int l,r;
    ll num;
}tree[4*N];

ll a[N];

//建树 O(logn)
inline void build(int l,int r,int i)
{
    tree[i].l=l;
    tree[i].r=r;
    tree[i].num=0;
    if(l==r)
        return ;
    int mid=(l+r)>>1;
    build(l,mid,i*2);
    build(mid+1,r,i*2+1);
}

//单点更新O(logn)   区间更新O(nlogn)
inline void update(int x,int l,int r,int i)
{
    if(tree[i].l==tree[i].r)
    {
        tree[i].num++;
        return ;
    }
    int mid=(l+r)>>1;

    if(x<=mid)
        update(x,l,mid,i*2);
    if(x>mid)
        update(x,mid+1,r,i*2+1);
    tree[i].num=tree[i*2].num+tree[i*2+1].num;
}


//查询 O(logn)
inline ll query(int l,int r,int i)
{
    if(tree[i].l>=l&&tree[i].r<=r)
    {
        return tree[i].num;
    }
    if(tree[i].l>r||tree[i].r<l)
        return 0;
    ll s=0;
    int mid=(tree[i].l+tree[i].r)>>1;
    if(mid>=l)
        s+=query(l,r,i*2);
    if(mid<=r)
        s+=query(l,r,i*2+1);
    return s;
}

map<ll,ll> mp;
map<ll,ll>::iterator it;
ll tt,n,te;
int main()
{
    ios::sync_with_stdio(false);
    cin>>tt;
    while(tt--)
    {
        mp.clear();
        cin>>n;
        ll ans=0,cnt=0;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            mp[a[i]]=1;
        }
        for(it=mp.begin();it!=mp.end();it++)
        {
            it->second=++cnt;
        }
        for(int i=1;i<=n;i++)
        {
            a[i]=mp[a[i]];
        }
        build(1,mp.size(),1);
        for(int i=1;i<=n;i++)
        {
            update(a[i],1,mp.size(),1);
            ll mmin=query(1,a[i]-1,1);
            ll mmax=i-query(1,a[i],1);
            ans+=min(mmin,mmax);
        }
        cout<<ans<<endl;
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值