[ACM] 训练赛题解

2021_积分赛第二场

名称 来源 算法
敌兵布阵 HDU-1166 线段树(简单)
Emoticons ICPC Central Russia Regional Contest (CRRC 19) map 模拟
Power play ICPC Central Russia Regional Contest (CRRC 19) 浮点二分(卡精度)
Prinzessin der Verurteilung CodeForces 1536B 思维题
Vases and Flowers HDU 4614 线段树 + 二分
Omkar and Bad Story CodeForces 1536A 思维题

A - 敌兵布阵

题解

​ 题意中文没啥好说的,多组测试样例,对输入的数组进行区间查询单点修改,就是专门留给你们的线段树板子题。

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

const int N = 50005;

struct Node
{
   int l,r;
   int sum;
}T[N<<2];

int a[N];

void push_up(int rt)
{
   T[rt].sum = T[rt << 1].sum + T[rt << 1|1 ].sum;
}

void build(int rt,int l,int r)
{
   T[rt] = {l,r,0};
   if( l == r ){
       T[rt].sum = a[l];
       return ;
   }

   int mid = (l + r) >>1;
   build(rt << 1,l,mid),build(rt << 1|1,mid + 1,r);
   push_up(rt);
}

void update(int rt,int pos ,int val)
{
   if( T[rt].l == T[rt].r ){
       T[rt].sum += val;
       return ;
   }

   int mid = (T[rt].l + T[rt].r) >>1;
   if( pos <= mid ) update(rt << 1,pos,val);
   else update( rt << 1|1,pos ,val );
   push_up(rt);
}

int query(int rt,int l,int r)
{
   if( l <= T[rt].l && r >= T[rt].r ){
       return T[rt].sum;
   }

   int mid = ( T[rt].l + T[rt].r ) >> 1;
   int son = 0;

   if( l <= mid ) son += query(rt << 1,l,r);
   if( r > mid ) son += query(rt << 1|1,l,r);
   return son;
}

void solve(){
   int n;
   cin >> n;
   for(int i = 1 ; i <= n ; i++){
       cin >> a[i];
   }

   build(1,1,n);

   string op;
   while( cin >> op ){
       if( op == "End" ) break;
       int x,y;
       cin >> x >> y;

       if( op == "Query" ){
           cout << query(1,x,y) <<"\n";
       }else if( op == "Add" ){
           update( 1,x,y );
       }else{
           update( 1,x,(-1)*y);
       }
   }

}

int main()
{
   ios::sync_with_stdio(false);
   cin.tie(nullptr);
   cout.tie(nullptr);

   int t;
   cin >>t;
   for(int i = 1 ; i <= t ; i++){

       cout << "Case "<<i<<":"<< "\n";
       solve();
   }

   return 0;
}

B - Emoticons

题意

​ 一个叫 Basil 的程序员,正在写一个新的文本编辑器。为了查看邮件,Basil编写了一个特殊的模块,该模块从文本文件中提取所有表情符号,并将它们放在单独的行中显示。不幸的是,模块中出现了一个错误,表情符号中的字符混淆了。

​ Basil 知道以下的这些表情都应用到了信件中:

;-);-(:):(:-\:-P

:D:C:-0:-|8-0:-E

%0:-X:∼([:|||:]

​ 帮助 Basil, 写一个程序,把读入的乱序表情,恢复正常。如果有几种可能的恢复选项,那么其中任何一种都是正确的。

题解

​ 就是一道大模拟题,用map来存每个字符出现了多少次。

​ 由分析后我们可以知道,有的表情的组合优先级是高于其他表情的(也就是说,我们在组合的时候要优先去组合他)。

​ 有的符号是独一无二的,他只要出现,就唯一对应一个表情,那么其他非唯一的表情就是肯定会给他优先消耗。就比如说 D , C , P , 8 ... 只要这些字符出现,那么它就唯一的对应 :D,:C ... 这些表情,我们此时就把这个表情所涉及到的字符都在map中 -- ,就好了。

​ 注:\字符,要两个才可以\\ ,此为转义字符

注意:这段代码非常非常繁琐,模拟题就是来恶心人的,你wa了,就是细节错了,我们当时做的时候,他们都放弃了,我最后看了半天才发现那里错。

#include <bits/stdc++.h>

using namespace std;
const int N = 1e3 + 5;
int a[N];

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    string s;
    cin >> s;
    int len = s.size();
    int cnt = 0;

    map<char, int> ma;
    ma['['] = 0, ma[':'] = 1;
    ma[']'] = 2, ma['|'] = 3;
    ma['D'] = 4, ma['C'] = 5;
    ma['P'] = 6, ma['E'] = 7;
    ma['8'] = 8, ma['0'] = 9;
    ma['%'] = 10, ma['X'] = 11;
    ma['~'] = 12, ma['\\'] = 13;
    ma['-'] = 14, ma['('] = 15;
    ma[';'] = 16, ma[')'] = 17;


    for(int i = 0; i < len; i ++ ){
        a[ma[s[i]]] ++;
    }

    while(a[0]){
        cout << "[:|||:]" << endl;
        a[0] --, a[1] -= 2, a[2] --;
        a[3] -= 3;
    }

    while(a[4]){
        cout << ":D" << endl;
        a[4] --, a[1] --;
    }

    while(a[5]){
        cout << ":C" << endl;
        a[5] --, a[1] --;
    }

    while(a[6]){
        cout << ":-P" << endl;
        a[6] --, a[1] --, a[14] --;
    }

    while(a[7]){
        cout << ":-E" << endl;
        a[14] --, a[1] --, a[7] --;
    }


    while(a[3]){
        cout << ":-|" << endl;
        a[3] --, a[1] --, a[14] --;
    }

    while(a[8]){
        cout << "8-0" << endl;
        a[8] --, a[14] --, a[9] --;
    }

    while(a[10]){
        cout << "%0" << endl;
        a[9] --, a[10] --;
    }

    while(a[9]){
        cout << ":-0" << endl;
        a[9] --, a[1] --, a[14] --;
    }

    while(a[11]){
        cout << ":-X" << endl;
        a[11] --, a[1] --, a[14] --;
    }

    while(a[12]){
        cout << ":~(" << endl;
        a[12] --, a[1] --, a[15] --;
    }

    while(a[13]){
        cout << ":-\\" << endl;
        a[13] --, a[1] --, a[14] --;
    }

    while(a[14] && a[16] && a[15]){
        cout << ";-(" << endl;
        a[16] --, a[14] --, a[15] --;
    }

    while(a[14] && a[16] && a[17]){
        cout << ";-)" << endl;
        a[16] --, a[14] --, a[17] --;
    }

    while(a[1] && a[15]){
        cout << ":(" << endl;
        a[1] --, a[15] --;
    }

    while(a[1] && a[17]){
        cout << ":)" << endl;
        a[1] --, a[17] --;
    }

    cout << "LOL" << endl;

    return 0;
}

C - Power play

题意

​ basil 在分析一个数学问题的时候发现一个有趣的现象, 对于数字 2,4 满足 $ 2^4 = 4^2$​ 。他认为这对编程设计大赛的参赛者来说可能是一个巨大的挑战。不幸的是,Basil 不能发现另外的这样的数对了,他认为对于满足这种关系的数对,他只能找到2,4。好吧,那我们就改变条件,有三个数字。Basil 决定。编写的枚举选项程序证实了三个数字的任务是有意义的。

​ 你的任务是:找到一个 x 其范围在 \(1 \le x\le1e^{18}\) ,满足 $ a^x = x^b$​ (a,b为输入的数字),答案可能有很多任意输出满足条件的答案即可,如果没有这样的数存在那么输出0 。​

题解

​ 由题目我们得到这个公式

\[a^x = x^b \]

​ 我们对两边取对数得到

\[x\times log(a) =b\times log(x) \]

​ 移项可得

\[x = \frac{ b \times log(x) }{log(a)} \]

​ 然后我们二分x的值,就可以了

​ 单调性证明略

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL a, b;
LL f(LL l, LL r) 
{
    if (l > r) return 0;
    LL mid = (l + r) / 2;
    double s=double(log(mid)/log(a));
    if (abs(s * b - mid) < 0.00000000001) 	///看是否有比他更小的存在 
    {
        LL ans = f(1, mid - 1);
        if (ans) return ans;
        return mid;
    }
    if (mid > s * b)  return f(l, mid - 1);
    return f(mid + 1, r);
}
int main() 
{
    int flag = 1;
    scanf("%lld%lld", &a, &b);
    printf("%lld\n", f(1, 1e18));
}

\[ \]

D - Prinzessin der Verurteilung

题意

​ 给你一个字符串,让你按字典序求第一个没在这个字符串中出现的子串,(由小到大排列的顺序为 a b c d e f ... z ... aa ab ac ad ... ),在样例qaabzwsxedcrfvtgbyhnujmiklop 中,az 都 出现了, ab 出现了, ac 没有出现,那么答案就是 ac

题解

​ 因为数据范围不大,所以我们直接上 STL 就好了 ,stringfind 函数可以解决

​ 我们建立一个 vector<string >数组来遍历的字典序字符串,然后对于每个字符串我们都在其后面26个字母

​ 对于""空字符串,我们加26个字母后变成,abc,...,z,对于a字符串我们加了26个字符串后变成 aaabac,...,az以此类推。

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

typedef long long ll;
const int N = 1e5 + 10;

    /// 遍历的模板
string str = "abcdefghijklmnopqrstuvwxyz";

void solve()
{
    int n;
    cin >> n;
    string s;
    cin >> s;
    /// 建立字符串数组,同时把第一个字符串初始化为空字符串
    vector<string> bfs = {""};
    for(int i = 0; i < bfs.size() ; i++){

        string te = bfs[i];
        /// 找到了就直接输出
        if( s.find(te) == string::npos ){
            cout << te << "\n";
            break;
        }
        /// 对于每个字符串,我们都在起后面加上26个字母
        for( auto it : str ){
            bfs.push_back(te+it);
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cout.tie(nullptr);
    cin.tie(nullptr);

    int t;
    cin>> t;
    while(t--){
        solve();
    }

}

E - Vases and Flowers

题意

​ 给定一个整数n, 表示有n个花瓶(初始为空花瓶), 编号从0~n-1. 有如下两种操作:

​ ①从编号为x的花瓶开始, 要放y朵花, 从前往后一次遍历, 如果是空花瓶则放一朵花在里面, 直至放完所有花或者遍历到最后一个花瓶为止. 倘若此时还有花放不下, 则将它们直接丢弃.
​ ②清理[l, r]区间的所有花瓶, 如果里面有花则将其丢弃

​ 对于每个操作①, 需要输出第一个放花的位置和最后一个放花的位置. 倘若一朵花都放不下, 需要输出"Can not put any one."
​ 对于每个操作②, 需要输出该区间被清理的花的数量。

题解

​ 我们用线段树来维护这个花瓶,线段树的每一个结点,就相当于一个花瓶。

​ 这道题最关键的点就是,如何设置状态,我们用sum来表示区间的剩余可插花数,用lazy来标记区间修改的操作 lazy = -1,为不变,lazy = 0清除插花,lazy = 1待插花。

​ 对于操作①,我们在线段树上递归的进行二分,如果当前区间在我们需要插花的区间,并且可插花数 <= 剩余的待插花树,那么我们就在这个区间中二分的查找需要最左边的插花位置,和最右边的插花位置,并与全局变量 L,R 比较,最后输出这次的L,R。

​ 对于操作②,相当于rangeQuery + rangeUpdate,在更新区间插花的同时,计数有那些结点插了花,并返回。

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

const int N = 5e5 + 10;

struct Node
{
   int l,r;
   int sum,lazy;   ///sum为当前剩余可插画数
   ///lazy = -1,为不变,lazy = 0清除插花,lazy = 1待插花
}t[N << 2];
int L,R;

void push_up(int rt){ t[rt].sum = t[rt << 1].sum + t[rt << 1|1].sum; }

void build(int rt,int l, int r)
{
   t[rt] = {l,r,1,-1};
   if( l == r )    return ;
   int mid = (l + r) >> 1;
   build(rt << 1,l,mid),build(rt<<1|1,mid +1 ,r);
   push_up(rt);
}

void push_down(int rt)
{
   if( t[rt].lazy == -1 ) return;
   int lazy = t[rt].lazy ,l = t[rt].l,r = t[rt].r;
   int mid = l + r >> 1;

   t[rt<<1].sum = lazy * (mid - l +1);
   t[rt<<1|1].sum = lazy * (r - mid);
   t[rt << 1|1].lazy = t[rt << 1].lazy = lazy;
   t[rt].lazy = -1;
}

///查询插花区间最左边的位置
int findLeft(int rt)
{
   if( t[rt].l == t[rt].r ) return t[rt].r;
   push_down(rt);
   if( t[rt << 1].sum != 0 ) findLeft(rt << 1);
   else findLeft(rt << 1|1);
}
///查询插花区间最右边的位置
int findRight(int rt)
{
   if( t[rt].l == t[rt].r ) return t[rt].l;
   push_down(rt);
   if( t[rt << 1|1].sum != 0 )    findRight(rt <<1|1);
   else findRight(rt << 1);
}

void rangeUpdate(int rt,int l,int r,int &val)
{
   if( val == 0 || t[rt].sum == 0 ) return;
   if( l <= t[rt].l && r >= t[rt].r && t[rt].sum <= val   )
   {
       val -= t[rt].sum;
       /// 更新第一个和最后一个
       L = min(L,findLeft(rt)) ,  R = max(R,findRight(rt));
       t[rt].sum = 0;
       t[rt].lazy = 0;
       return ;
   }
   int mid = t[rt].l + t[rt].r >> 1;
   push_down(rt);
   if( l <= mid )  rangeUpdate(rt << 1,l,r,val);
   if( r > mid )   rangeUpdate(rt << 1|1,l,r,val);
   push_up(rt);
}

int rangeDelte(int rt,int l,int r)
{
   if( l <= t[rt].l && r >= t[rt].r )
   {
       int res = t[rt].r - t[rt].l + 1 - t[rt].sum;
       t[rt].sum = t[rt].r - t[rt].l + 1;
       t[rt].lazy = 1;
       return res;
   }

   int mid = (t[rt].l + t[rt].r) >> 1;
   int res = 0;
   push_down(rt);
   if( l <= mid )  res += rangeDelte(rt << 1,l,r);
   if( r > mid )   res += rangeDelte(rt << 1|1,l,r);
   push_up(rt);
   return res;
}

int main()
{
   ios::sync_with_stdio(false);
   cin.tie(0);

   int n,t,m,x,y,cmd,te;
   cin >> t;
   while(t--)
   {
       cin >> n >> m;
       build(1,1,n);
       while(m--)
       {
           cin >> cmd >> x >> y;
           if(cmd == 1)
           {
               L = n + 1, R = 0,te = y;
               rangeUpdate(1,x+ 1,n,y);
               if(te == y) cout << "Can not put any one." <<endl;
               else cout << L - 1 << ' ' <<R - 1 <<endl;

           }
           else cout << rangeDelte(1,x + 1,y + 1) << endl;
       }
       cout <<endl;
   }

   return 0;
}

F - Omkar and Bad Story

题意

​ Omkar收到了来自 Anton 的消息 "你对问题A的解释很混乱。再写个详细的说明 " 。正因如此, Omkar 给了你一个数组 a ,其有 n 个不同的数。如果一个数组(b)满足任意的两个元素 \(b_i,b_j\) ,\(|b_i - b_j|\)​ 在这个数组中至少出现了一次 ,那么这个数组就是一个nice 数组。此外,b中的所有元素必须是不同的。你能添加几个整数(可能是0)到a来创建一个大小不超过300的数组b 吗。如果a已经是个 nice数组了,你就不需要添加任何元素。

题解

如果原本数列中出现了负数,则直接输出NO,因为你有一个负数存在的情况下

假设原数列为1 和-1
-1 - 1的绝对值为2
-1 - 2的绝对值为3
可以看出新出现的数只会不断变大,所以是个无解

保证数列为正数的情况下直接输出0至100就好了,

优化一下就是输出最大的数

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

typedef long long ll;
const int N = 1e5 + 10;


void solve()
{
    int n;
    cin>>n;
    int a[n];
    bool sanu=false;
    int maxi=INT_MIN;
    for(int i=0; i<n; i++){
        cin >> a[i];
        maxi=max(maxi, a[i]);
        if(a[i] < 0) sanu = true;
    }
    if(sanu){
        cout << "NO\n";
        continue;
    }else {
        cout << "YES\n";
        cout << maxi+1 << endl;
        for(int i = 0;i <= maxi ; i++) cout << i << " ";
        cout << endl;
    }

}

int main()
{
    ios::sync_with_stdio(false);
    cout.tie(nullptr);
    cin.tie(nullptr);

    int t;
    cin>> t;
    while(t--){
        solve();
    }

}

上一篇:实验3 转移指令跳转原理及其简单应用编程


下一篇:linux root密码重置