Codeforces Round #704 (Div. 2)A,B,C,D

Codeforces Round #704 (Div. 2)

A - Three swimmers

题意

三个人游泳,从左侧台子出发,来回一次的时间分别是 a , b , c a, b, c a,b,c 分钟,他们在泳池里不间断的来回。你在第 p p p 分钟到达,问第一个出现在左侧的人至少要多少时间才出现?

思路

分别计算三个人各需要多久回来,取最小。

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 1e6 + 9;
const ll MOD = 1e9 + 7;

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        ll a, b, c, p;
        scanf("%lld%lld%lld%lld", &p, &a, &b, &c);
        a = (p / a + (p % a > 0 ? 1 : 0)) * a - p;
        b = (p / b + (p % b > 0 ? 1 : 0)) * b - p;
        c = (p / c + (p % c > 0 ? 1 : 0)) * c - p;
        cout << min(a, min(b, c)) << endl;
    }
    return 0;
}

B - Card Deck

题意

n n n 张卡片叠成一堆,每张上都写了数字,从下往上的第 i i i 张上写着 p i p_i pi​ 。现在要求你按照以下规则重新排列卡片,使得 ∑ i = 1 n n n − i ⋅ p i \sum_{i = 1} ^n n ^{n - i} · p_i ∑i=1n​nn−i⋅pi​ 的值最大:

  1. 选取从上往下数的第 i i i 张;
  2. 把第 i i i 张即其上方的所有卡片一起放到新的一堆的顶上;
  3. 一直重复,直到原来的那堆为空

思路

可以证明,最优情况一定是每次选取原堆剩余中的最大的元素所在的位置,移动其及其上的所有卡片(可以自己比较一下对于7 1 2 3 4 5 6,是6在底下还是7在底下大)。

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 1e6 + 9;
const ll MOD = 1e9 + 7;

struct node
{
    int data;
    int id;
}a[N];

bool vis[N];
int num[N];

bool cmp(node& a, node& b)
{
    if(a.data == b.data)
        return a.id > b.id;
    return a.data > b.data;
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        int n;
        scanf("%d", &n);
        fill(vis, vis + n + 1, 0);
        for(int i = 0; i < n; i++)
        {
            scanf("%d", &a[i].data);
            num[i] = a[i].data;
            a[i].id = i;
        }
        sort(a, a + n, cmp);
        for(int i = 0; i < n; i++)
        {
            if(vis[a[i].id])
                continue;
            for(int j = a[i].id; j < n; j++)
            {
                if(vis[j])
                    break;
                printf("%d ", num[j]);
                vis[j] = 1;
            }
        }
        printf("\n");
    }
    return 0;
}

C - Maximum width

题意

给定字符串 s , t s, t s,t (长度分别为 n , m n, m n,m ),要求你构造一个序列 p p p ,使得 1 ≤ p 1 < p 2 < ⋯ < p m ≤ n 1 \leq p_1 \lt p_2 \lt \dots \lt p_m \leq n 1≤p1​<p2​<⋯<pm​≤n 且对于所有的 i ∈ [ 1 , m ] i \in [1, m] i∈[1,m] ,有 s p i = t i s_{p_i} = t_i spi​​=ti​。现在你要输出 m a x ( p i + 1 − p i ) max(p_{i + 1} - p_i) max(pi+1​−pi​) 的最大值。

思路

记录每个点可以取的左右限,这个左右限受三个因素影响:第一, t i t_i ti​ 在 s s s 中出现位置的左右限;第二, t i − 1 t_{i - 1} ti−1​ 的左限要小于 t i t_i ti​ 的左限;第三, t i + 1 t_{i + 1} ti+1​ 的右限要大于 t i t_i ti​ 的右限。只有二、三满足了,序列 p p p 的元素才能递增。

最后只要取 m a x ( t i + 1 r − t i l ) max(t_{{i + 1}_r} - t_{i_l}) max(ti+1r​​−til​​) 即可,即下一个元素的最大值-上一个元素的最小值。

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 1e6 + 9;
const ll MOD = 1e9 + 7;

char s[N], t[N];

struct node
{
    vector<int> pos;
    int id = 0;
}a[N];

struct node_
{
    int l, r;
}b[N];

int main()
{
    int n, m;
    cin >> n >> m;
    cin >> s;
    cin >> t;
    for(int i = 0; i < n; i++)
    {
        a[s[i] - '0'].pos.push_back(i);
    }
    int ans = 0;
    b[m - 1].r = a[t[m - 1] - '0'].pos.back();
    for(int i = m - 2; i >= 0; i--)
    {
        while(a[t[i] - '0'].pos.back() >= b[i + 1].r)
            a[t[i] - '0'].pos.pop_back();//直接删除尾部,以后肯定用不到
        b[i].r = a[t[i] - '0'].pos.back();
    }
    b[0].l = a[t[0] - '0'].pos[0];
    for(int i = 1; i < m; i++)
    {
        int idx = a[t[i] - '0'].id;//因为直接删除首部元素不太方便,所以用下标记录了。其实r的更新也可以用下标记录
        while(a[t[i] - '0'].pos[idx] <= b[i - 1].l)
            idx++;
        b[i].l = a[t[i] - '0'].pos[idx];
        a[t[i] - '0'].id = idx;
    }
    for(int i = 0; i < m - 1; i++)
    {
        int x = b[i].l;
        int y = b[i + 1].r;
        ans = max(ans, y - x);
    }
    cout << ans << endl;
    return 0;
}

D - Genius’s Gambit

题意

有两个数,这两个数在二进制表示下0的个数相同、1的个数相同。给定0和1的个数 a , b a, b a,b,以及数字 k k k ,问是否有满足条件的两个数字,这两个数字相减得到的数字里1的个数恰好是 k k k 个?在二进制表示下输出两个数字,找不到答案输出-1。

思路

最简单的构造方法是相减得到的数恰好是 2 k − 1 2^k - 1 2k−1,即只有 k k k 个 1 1 1。假设个位是第 1 1 1 位,那么两个数字就在第 1 1 1 位和第 k k k 位不同,其他地方都相同,即:
{ 1 ⋯ ⏟ a + b − k − 2 1 ⋯ ⏟ k − 2 0 1 ⋯ ⏟ a + b − k − 2 0 ⋯ ⏟ k − 2 1 \left\{\begin{aligned} 1\underbrace{\cdots}_{\rm a + b - k - 2}1\underbrace{\cdots}_{\rm k - 2}0\\ \\ 1\underbrace{\cdots}_{\rm a + b - k - 2}0\underbrace{\cdots}_{\rm k - 2}1 \end{aligned} \right. ⎩⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎧​1a+b−k−2 ⋯​​1k−2 ⋯​​01a+b−k−2 ⋯​​0k−2 ⋯​​1​
因此,我们可以得到给定 a , b , k a, b, k a,b,k 有答案的充要条件:
{ a + b − k − 2 > 0 a > 2 b > 1 . \begin{cases} a + b - k - 2 > 0 \\ a > 2 \\ b > 1 \end{cases}. ⎩⎪⎨⎪⎧​a+b−k−2>0a>2b>1​.
特判 k = 0 k = 0 k=0 的情况,一定是成功的(两个数一样就行),再特判无答案的情况,其他的就按照上面构造即可。

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 1e6 + 9;
const ll MOD = 1e9 + 7;

int s[N], t[N];

int main()
{
    int a, b, k;
    cin >> a >> b >> k;
    if(k == 0)
    {
        cout << "Yes" << endl;
        for(int i = 0; i < b; i++)
        {
            cout << 1;
        }
        for(int i = 0; i < a; i++)
        {
            cout << 0;
        }
        cout << endl;
        for(int i = 0; i < b; i++)
        {
            cout << 1;
        }
        for(int i = 0; i < a; i++)
        {
            cout << 0;
        }
        cout << endl;
        return 0;
    }
    else if(k > a + b - 2 || a == 0 || b == 1)
    {
        cout << "No" << endl;
        return 0;
    }
    if(k == a + b - 2)
    {
        cout << "Yes" << endl;
        for(int i = 0; i < b; i++)
        {
            cout << 1;
        }
        for(int i = 0; i < a; i++)
        {
            cout << 0;
        }
        cout << endl;
        cout << 10;
        for(int i = 1; i < b - 1; i++)
        {
            cout << 1;
        }
        for(int i = 1; i < a; i++)
        {
            cout << 0;
        }
        cout << 1;
        cout << endl;
        return 0;
    }
    cout << "Yes" << endl;
    s[k] = 1;
    t[k] = 0;
    t[0] = 1;
    int cnt0 = 1, cnt1 = 1;
    for(int i = 1; i < a + b; i++)
    {
        if(i == k)
            continue;
        if(cnt0 < a)
        {
            cnt0++;
            s[i] = t[i] = 0;
        }
        else
        {
            s[i] = t[i] = 1;
        }
    }
    for(int i = a + b - 1; i >= 0; i--)
    {
        cout << s[i];
    }
    cout << endl;
    for(int i = a + b - 1; i >= 0; i--)
    {
        cout << t[i];
    }
    cout << endl;
    return 0;
}

总结

  1. B题第一次WA是简单错误,但是一WA就脑子空白,想不出错在哪,最后推翻重写。赛后重新看第一次的代码,一下就发现了;
  2. C题开始读题错误,导致错了很久,其实开始的方法是可行的(赛后也得到了验证),但是一直没有怀疑自己读错题,一直在检查代码,拖了更久才过;
  3. D题赛场上少判了一个b=1,但是当时有点坚持不住了,去看standing、看预估分去了,最后在比赛结束前5秒看出来错误了,但是根本来不及交上去了……
  4. 总而言之,就是心态很存在问题叭。还是要慢慢来,不害怕掉分!

多多包涵,共同进步

上一篇:Codeforces Round #704 (Div. 2)


下一篇:Codeforces Round #704 (Div. 2) E. Almost Fault-Tolerant Database