其他分享
首页 > 其他分享> > CF Round 699(Div2) 解题补题报告

CF Round 699(Div2) 解题补题报告

作者:互联网

官方题解

A题 Space Navigation(数学)

现在我们在坐标系上面,要从点 \((0,0)\) 到点 \((px,py)\),按照一个给定的操作序列 \(s\) 来走(例如 \(UURLLDR\),各字母分别对应上下左右)。不过,由于可能到不了终点,所以我们对其中的某些步可以选择走或者不走。问我们能否最后到终点。

\(-10^5 \leq px,py \leq 10^5,1\leq |s|\leq 10^5\)

记 \(up\) 为 \(U\) 的数量,\(down\) 为 \(D\) 的数量,\(left\) 为 \(L\) 的数量,\(right\) 为 \(R\) 的数量。

显然不管我们怎么选,最后到达的位置必然在 \((x,y)\) 上,其中 \(-left \leq x \leq right,-down \leq y \leq up\)

那我们看下终点在不在这个范围上就行了

#include <bits/stdc++.h>
using namespace std;
int px, py;
char s[100010];
bool solve()
{
    memset(s, 0, sizeof(s));
    scanf("%d%d%s", &px, &py, s);
    int up = 0, down = 0, left = 0, right = 0;
    for (int i = 0, Len = strlen(s); i < Len; ++i)
        switch (s[i]) {
            case 'U' : up++;    break;
            case 'D' : down++;  break;
            case 'L' : left++;  break;
            case 'R' : right++; break;
        }
    return (-left <= px && px <= right && -down <= py && py <= up);
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
        puts(solve() ? "YES" : "NO");
    return 0;
}

B题 New Colony(模拟)

有 \(n\) 座连续的山,第 \(i\) 座山高度为 \(h_i\)。现在有个人不停从第一座山开始滚石头,如果前一座山高度比自己现在这座高,那么这块石头就会留在这,并且垫在这里(相当于所在山的高度加 \(1\)),否则就一直滚下去。当石头达到最后一座山时,石头就会滚下悬崖。现在这个人想知道, 第 \(k\) 块石头最终会停在哪(或者滚下悬崖)。

\(n\leq 100,h_i\leq 100,1\leq k \leq10^9\)

显然,当山的高度单调不增的时候,之后的石头就全掉下去了。显然,想要把山填成这样,最极端情况也就 \(9900\) 个石头,没 \(10^9\) 这么夸张。

那很显然,我们对前 \(10000\) 个石头直接硬模拟就好了,每块石头都记一下能到哪。如果掉下去了就打破循环,退出来。

这玩意复杂度的上界是 \(O(100n^2)\) 的,完全可以过,不会 \(T\)(逃

#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int n, k, h[N];
int ans[10010];
void solve()
{
    memset(ans, 0, sizeof(ans));
    memset(h, 0, sizeof(h));
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &h[i]);
    int cnt = 0;
    while (1) {
        cnt++;
        int i;
        for (i = 1; i < n; ++i)
            if (h[i] < h[i+1]) {
                ans[cnt] = i, h[i]++;
                break;
            }
        if (i == n) break;
    }
    if (k >= cnt) printf("-1\n");
    else printf("%d\n", ans[k]);
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--) solve();
    return 0;
}

C题 Fence Painting

有一块篱笆,有 \(n\) 个板子,第 \(i\) 块板子的颜色为 \(a_i\)。现在我们觉得篱笆原来颜色丑的一,想要把第 \(i\) 块板子颜色涂成 \(b_i\)。

现在找来了 \(m\) 个工匠,第 \(i\) 个会在时刻 \(i\) 到来,选择一块板子并将其颜色涂成 \(c_i\),而且他肯定准时来并且一定要涂一块,不能不涂(敬业的一)。现在尝试安排一下,看看能不能如愿以偿的刷成希望的样子:能就输出方案,不能就输出 \(NO\)

\(1\leq n,m \leq 10^5,1 \leq a_i,b_i,c_i \leq n\)

显然,我们要对每一组 \(a_i\) 和 \(b_i\) 判断一下是不是一样,一样就暂时不管,否则就加入 \(ToDo\) 清单里面,准备让工匠来处理下。如果发现有的板子的颜色,工匠没法处理,那显然就无解了。

另外一个难缠的点在于,有些工匠明明不需要,但是这些人非得来刷一下,多花钱就算了,还可能会把好好的篱笆给刷坏掉,就离谱。

我们考虑一下最后一个工匠:

话说如果是第二类,那么我们想到一个策略:前面没用的工匠可以都来刷最后一块剩下的墙,然后让最后一个工匠来处理好这块墙就行了

如果是第一类,那么也有一种策略:选好这块相同颜色的板子,然后前面没用的工匠也全部安排来刷这块板子就好了

那么最后一个工匠的策略就很简单了:找到一个目标颜色和其刷的颜色相同的板子,然后在其来的时候刷这块板子。对于前面没啥用的工匠,就来刷这块板子(反正最后一个工匠会把他刷成正确的颜色)

对了,如果最后一个工匠有多种选择,要优先从 \(a_i \not= b_i\) 的板子里面选(这玩意就不多解释了,懂得都懂)

至于如何存储每个工匠可以刷什么板子,我用了邻接表的方式,开了 \(n\) 个邻接表,对应不同颜色,然后每个颜色的邻接表插入对应颜色板子的序号。

详情请见代码(比赛的时候有这个思路,但是被代码实现逼疯了):

#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, m, a[N], b[N], c[N], ans[N];
int tot, Head[N], Next[N<<1], ver[N<<1];
void add(int color, int num) {
    ver[++tot] = num, Next[tot] = Head[color], Head[color] = tot;
}
void solve()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &b[i]);
    for (int i = 1; i <= m; ++i)
        scanf("%d", &c[i]);
    memset(ans , 0, sizeof(int) * ( m  + 1));
    memset(Next, 0, sizeof(int) * (tot + 1));
    memset(ver,  0, sizeof(int) * (tot + 1));
    memset(Head, 0, sizeof(int) * ( n  + 1));
    tot = 0;

    for (int i = 1; i <= n; ++i)
        if (a[i] != b[i] && b[i] == c[m]) {
            a[i] = b[i] = c[m];
            ans[m] = i;
            break;
        }
    if (!ans[m]) {
        for (int i = 1; i <= n; ++i)
            if (b[i] == c[m]) {
                a[i] = b[i] = c[m];
                ans[m] = i;
                break;
            }
    }
    if (!ans[m]) { puts("NO"); return; }

    for (int i = 1; i <= n; ++i)
        if (a[i] != b[i]) add(b[i], i);

    int cnt = tot;
    for (int i = m - 1; i >= 1; i--) {
        if (Head[c[i]]) {
            ans[i] = ver[Head[c[i]]];
            Head[c[i]] = Next[Head[c[i]]];
            cnt--;
        }
        else ans[i] = ans[m];
    }
    if (cnt) { puts("NO"); return; }
    puts("YES");
    for (int i = 1; i < m; ++i)
        printf("%d ", ans[i]);
    printf("%d\n", ans[m]);
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--) solve();
    return 0;
}

D题 AB Graph

给定一个完全有向图,共 \(n\) 个点,\(n(n-1)\) 条边(两节点之间的边算两个不同的有向边),每个边都有一个字符 \(a\) 或者 \(b\)。

现在我们想要找出一条长度为 \(m\) 的路,沿着路上字符组成的字符串是回文的,问能否找出?能找出的话,要找出一条并打印出来。

\(2\leq n \leq 1000, m \leq 10^5\)

如果能找到两个点 \(x,y\),使得 \(x\) 到 \(y\) 边上和 \(y\) 到 \(x\) 边上的字符一样,那就太好了:沿着 \(x\) 和 \(y\) 来回走就好了。

反之,那么整个图上面,两个节点之间来回的字符都不一样。

如果 \(m\) 是奇数,那么有一个不太显然的解法:还是沿着两个点来回走,肯定是 \(ababab.....\) 这样的形式,而其长度\(m\) 是奇数的时候,显然是一个回文串(逃

\(m\) 是偶数的时候似乎就有点离谱了:建议直接看官方题解,我就不写了(逃

标签:颜色,int,699,工匠,CF,leq,补题,ans,板子
来源: https://www.cnblogs.com/cyhforlight/p/14382842.html