ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【解题报告】CF 704 (Div. 2) | ABCD

2021-02-23 22:32:01  阅读:195  来源: 互联网

标签:ABCD __ le 704 CF st int push ___


【解题报告】 CF 704 div2 | ABCD

【补充】

A:Three swimmers | 数学

  • 【题意】
    三个人同时开始无限跑圈,三个人分别一圈跑 a 、 b 、 c a、b、c a、b、c 分钟。
    问开始后第 p p p 分钟,在起点处最少等多少时间能遇到下一个人。
  • 【范围】
    样例组数 T ≤ 1000 T\le 1000 T≤1000
    1 ≤ p , a , b , c ≤ 1 0 18 1\le p,a,b,c\le 10^{18} 1≤p,a,b,c≤1018
  • 【思路】赛内
    签到题,推出最简的式子之后判断即可。
    每个人的跑一圈时间相当于循环节,用时间取模循环节,就是目前的进度。
    用循环节减去目前的进度就是剩余时间。
    注意目前进度如果为 0 0 0 表示已经在起点,再取模即可。
  • 【代码】
    时间复杂度: O ( T ) O(T) O(T)
/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 2e5+50;

int main()
{
    int T;scanf("%d",&T);
    while(T--){
        ll p,a,b,c;scanf("%lld%lld%lld%lld",&p,&a,&b,&c);
        a = (a - p % a) % a;
        b = (b - p % b) % b;
        c = (c - p % c) % c;
        printf("%lld\n",min(min(a,b),c));
    }
    return 0;
}

B:Card Deck | 贪心

  • 【题意】
    有 n n n 张牌, p i p_i pi​ 表示从牌底到牌顶第 i i i 张牌的编号。编号是个 n n n 的全排列。
    你可以第一堆卡,选择牌顶的 k k k 张牌,按照这个顺序把在这几张牌放在终点堆。
    最后第一堆卡被拿空了,得分为终点堆按这个式子计算: ∑ i n n − i × p i \sum_i n^{n-i}\times p_i ∑i​nn−i×pi​
  • 【范围】
    1 ≤ n ≤ 1 0 5 1\le n\le 10^5 1≤n≤105
  • 【思路】赛内
    首先随便看看,式子按照 i i i 增加,左边的大系数会递减。我们就希望越靠近前面的 i i i, p i p_i pi​ 越大。
    然后看一下样例,就随便得到了结论:
    假设拿牌的范围为 [ s t , e d ] [st,ed] [st,ed],我们的要求是 ∀ i ∈ [ s t , e d ] \forall i\in[st,ed] ∀i∈[st,ed] 满足 p s t ≥ p i p_{st}\ge p_i pst​≥pi​ 并且 p s t < p e d + 1 p_{st}<p_{ed+1} pst​<ped+1​
    具体原因的话,因为此时拿 p e d + 1 p_{ed+1} ped+1​ 放到前面更优啥的。
    用队列模拟一下就行了。
  • 【代码】
    时间复杂度: O ( n ) O(n) O(n)
/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 2e5+50;
const int INF = 0x3f3f3f3f;

int aa[MAX];
deque<int>Q;
int main()
{
    int T;scanf("%d",&T);
    while(T--){
        int n;scanf("%d",&n);
        for(int i = 1;i <= n;++i)scanf("%d",&aa[i]);
        for(int i = 1;i <= n;++i){
            int st = i;int ed = i;
            while(ed + 1 <= n && aa[ed+1] < aa[st])ed++;
            for(int j = ed;j >= st;--j)Q.push_front(aa[j]);
            i = ed;
        }
        while(!Q.empty()){
            printf("%d ",Q.front());
            Q.pop_front();
        }
        puts("");
    }
    return 0;
}

C:Maximum width | 贪心 + 思维

  • 【题意】
    给你两个字符串 s 、 t s、t s、t,然后 n = ∣ s ∣ , m = ∣ t ∣ n=|s|,m=|t| n=∣s∣,m=∣t∣
    你需要得到一个序列 p [ 1 , 2 , ⋯   , m ] p[1,2,\cdots,m] p[1,2,⋯,m],满足 1 ≤ p 1 < p 2 < ⋯ < p m ≤ n 1\le p_1<p_2<\cdots<p_m\le n 1≤p1​<p2​<⋯<pm​≤n,
    然后 ∀ i ∈ [ 1 , m ] \forall i\in[1,m] ∀i∈[1,m],满足 s p i = t i s_{p_i}=t_i spi​​=ti​
    定义该序列的宽度为 max ⁡ i { p i + 1 − p i } \max_i\{p_{i+1}-p_i\} maxi​{pi+1​−pi​}
    保证该序列至少存在一个。
    你需要给出宽度的最大值
  • 【范围】
    2 ≤ m ≤ n ≤ 2 × 1 0 5 2\le m\le n\le 2\times 10^5 2≤m≤n≤2×105
  • 【思路】赛内
    挺有意思的题目。首先那个序列相当于是 L C S LCS LCS 最长公共子序列的感觉。
    但是要求宽度最大,一时不知道哪个字母该对应哪个字母。
    然后我们想到了,对于某一个位置 i i i ,该位置之前的字母都优先左匹配,该位置之后的字母都优先右匹配
    在这里插入图片描述
    这样时间复杂度不够。
    那我们就想到了:位置 i i i 一步一步挪过来,貌似只影响一个字母的匹配位置。
    假设一开始所有的字母都是优先左匹配,然后倒着去处理,把最右边的优先左匹配的点改成优先右匹配。然后去求间隔的最大值。
    优先右匹配,就是去记录每一个字母出现的最右合法位置,可以用一个队列 q u e u e queue queue 去处理。
    那么什么位置是不合法的呢?比如目前该位置的字母的最右合法位置为 y o u you you ,那么接下来的所有字母的右匹配位置都不能在 y o u you you 位置的右边。
  • 【代码】
    时间复杂度: O ( ∣ n ∣ + ∣ m ∣ ) O(|n| + |m|) O(∣n∣+∣m∣)
/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 2e5+50;
const int INF = 0x3f3f3f3f;

int n,m;
string s,t;
int pos[MAX];
int ans;
void match(){		/// 优先左匹配,记录 p[]
    int st = 1;
    pos[0] = INF;
    for(int i = 1;i <= m;++i){
        while(s[st] != t[i])st++;
        pos[i] = st;
        st++;
        ans = max(ans,pos[i] - pos[i-1]);
    }

}
queue<int>last[30];
void solve(){
    int R = m;
    int hou = INF;
    for(int i = n;i >= 1;--i){
        if(i > pos[R]){				/// 这些字母的位置是可以用的
            last[s[i] - 'a'].push(i);
        }else{						/// 优先右匹配
            while(last[s[i] - 'a'].size() && last[s[i] - 'a'].front() > hou)last[s[i] - 'a'].pop();
            if(last[s[i] - 'a'].empty()){		/// 右匹配的位置就是目前位置
                hou = i;
            }else{
                pos[R] = last[s[i] - 'a'].front();		/// 右匹配该字母最右合法位置
                hou = last[s[i] - 'a'].front();			/// 最后位置更新
                last[s[i] - 'a'].pop();					/// 该位置没了
                last[s[i] - 'a'].push(i);				/// 多了一个新位置
                ans = max(ans,pos[R] - pos[R-1]);
            }

            R--;
            if(R == 0)break;
        }
    }
}
int main()
{
    cin >> n >> m >> s >> t;
    s = " " + s;
    t = " " + t;
    match();
    show(ans);
    solve();
    cout << ans;
    return 0;
}

D:Genius’s Gambit | 位运算 + 构造

  • 【题意】
    给你数字 a , b , k a,b,k a,b,k
    你需要构造两个二进制串 x , y x,y x,y,满足他们没有前导零,且 x ≥ y x\ge y x≥y
    首先,这两个串的 0 0 0 的个数都等于 a a a
    其次,这两个串的 1 1 1 的个数都等于 b b b
    其次, ( x − y ) 2 (x-y)_2 (x−y)2​ 的 1 1 1 的个数等于 k k k,就是两个数字相减后的二进制表示的 1 1 1 的个数为 k k k。
    问你怎么构造,或者无法构造。
  • 【范围】
    0 ≤ a 0\le a 0≤a
    1 ≤ b 1\le b 1≤b
    0 ≤ k ≤ a + b ≤ 2 × 1 0 5 0\le k\le a+b\le 2\times 10^5 0≤k≤a+b≤2×105
  • 【思路】赛内 + 赛后 ( % % % s o l e m n t e e % % % ) \color{red}(\%\%\%solemntee\%\%\%) (%%%solemntee%%%)
    默认以下竖式都是减法
    首先有这么几种简单的消耗操作
    1111 1111 − − − − − − 0000 \begin{matrix} 1111\\ 1111\\ ------\\ 0000 \end{matrix} 11111111−−−−−−0000​
    可以把两个串的多余的 1 1 1 消耗掉。
    0000 0000 − − − − − − 0000 \begin{matrix} 0000\\ 0000\\ ------\\ 0000 \end{matrix} 00000000−−−−−−0000​
    可以把两个串的多余的 0 0 0 消耗掉。
    也就是说, a , b a,b a,b 明显是少了不顶用,多了没关系的。接下来看怎么产生 1 1 1。
    1000 0001 − − − − − − 0111 \begin{matrix} 1000\\ 0001\\ ------\\ 0111 \end{matrix} 10000001−−−−−−0111​
    这是一个产生 1 1 1 的关键式子。消耗了 a a a 个 0 0 0 和一个 1 1 1 ,就可以产生 a a a 个 1 1 1
    然后我们按照这个思路去做, k k k 的上限为 a a a ,在做一些特判,结果 W a Wa Wa 了。
    为啥呢?我们还有更神奇的式子
    1011100 0011101 − − − − − − 0111111 \begin{matrix} 1011100\\ 0011101\\ ------\\ 0111111 \end{matrix} 10111000011101−−−−−−0111111​
    消耗了 a a a 个 0 0 0 和 b b b 个 1 1 1 ,就可以产生 a + b − 1 a+b-1 a+b−1 个 1 1 1。这是具有决定意义的式子。
    然后由于 y y y 串不能有前导零,必须每个式子花掉一个 1 1 1
    于是 k k k 的最大上限为: a + b − 2 a+b-2 a+b−2,前提是 b ≥ 2 b\ge 2 b≥2
    【总结一下】
    k = 0 k=0 k=0 的时候有些特殊,只要前面全放 1 1 1 后面全放 0 0 0 就行了。
    k > 0 k>0 k>0 的时候, 1 1 1 必须要用两个(一个开头一个产生 1 1 1), 0 0 0 必须要用一个(为了产生 1 1 1),上限为 a + b − 2 a+b-2 a+b−2
    首先我们需要拿一个 1 1 1 作为开头。接下来产生剩余的 1 1 1。
    然后我们构造
    1 x x x x x 0 0 x x x x x 1 − − − − − − 0111111 \begin{matrix} 1xxxxx0\\ 0xxxxx1\\ ------\\ 0111111 \end{matrix} 1xxxxx00xxxxx1−−−−−−0111111​
    ,其中 x x x 的要求:上下都为 1 1 1 或上下都为 0 0 0 都可。
    最后一些多余的 1 、 0 1、0 1、0,我们直接消耗掉即可。
    即最终的解长得样子为:
    11 x x x x x 01 ⋯ 10 ⋯ 0 10 x x x x x 11 ⋯ 10 ⋯ 0 − − − − − − − − − − − 0011111110 ⋯ 00 ⋯ 0 \begin{matrix} 11xxxxx01\cdots10\cdots0\\ 10xxxxx11\cdots10\cdots0\\ -----------\\ 0011111110\cdots00\cdots0 \end{matrix} 11xxxxx01⋯10⋯010xxxxx11⋯10⋯0−−−−−−−−−−−0011111110⋯00⋯0​
  • 【代码】
    时间复杂度: O ( a + b + k ) O(a+b+k) O(a+b+k)
/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 2e5+50;

queue<int>A,B;
int main()
{
    int a,b,k;cin >> a >> b >> k;
    if(k == 0){
        for(int i = 1;i <= b;++i)
            A.push(1),B.push(1);
        for(int i = 1;i <= a;++i)
            A.push(0),B.push(0);
        puts("Yes");
        while(!A.empty())cout << A.front(),A.pop();
        puts("");
        while(!B.empty())cout << B.front(),B.pop();
        return 0;
    }
    if(a + b - 2 < k || b == 1 || a == 0)puts("No");
    else{
        b--;
        A.push(1);B.push(1);
        a--;b--;
        A.push(1);B.push(0);
        k--;
        for(int i = 1;i <= k;++i){
            if(b){
                b--;
                A.push(1);B.push(1);
            }else{
                a--;
                A.push(0);
                B.push(0);
            }
        }
        A.push(0);B.push(1);

        puts("Yes");
        for(int i = 1;i <= b;++i)
            A.push(1),B.push(1);
        for(int i = 1;i <= a;++i)
            A.push(0),B.push(0);
        while(!A.empty())cout << A.front(),A.pop();
        puts("");
        while(!B.empty())cout << B.front(),B.pop();

    }
    return 0;
}

标签:ABCD,__,le,704,CF,st,int,push,___
来源: https://blog.csdn.net/weixin_45775438/article/details/114003458

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有