[Codeforces Round #617 (Div. 3)] 题解 A,B,C,D,E1,E2,F

1296A - Array with Odd Sum

思路:

如果一开始数组的sum和是奇数,那么直接YES,

否则:如果存在一个奇数和一个偶数,答案为YES,否则为NO

代码:

int n;
int a[maxn];
 
int main()
{
    //freopen("D:\\code\\text\\input.txt","r",stdin);
    //freopen("D:\\code\\text\\output.txt","w",stdout);
    int t;
    t = readint();
    while (t--)
    {
        n = readint();
        int sum = 0;
        repd(i, 1, n) {
            a[i] = readint();
            sum += a[i];
        }
        if (sum & 1)
        {
            printf("YES\n");
        } else
        {
            int isok1 = 0;
            int isok2 = 0;
            repd(i, 1, n)
            {
                if (a[i] & 1)
                {
                    isok1 = 1;
                } else
                {
                    isok2 = 1;
                }
            }
            if (isok2 == 1 & isok1 == 1)
            {
                printf("YES\n");
            } else
            {
                printf("NO\n");
            }
        }
    }
 
    return 0;
}

B. Food Buying

思路:

直接贪心思想模拟即可,

如果 s>9,那么花掉 \(s-s mod 10\) 的钱,还剩下 s%10+s/10 ,继续迭代。

否则 直接花掉所有钱,剩下0元,结束。

Time complexity: \(O(log_{10}(s))\)per test case.

代码:

 
int main()
{
    //freopen("D:\\code\\text\\input.txt","r",stdin);
    //freopen("D:\\code\\text\\output.txt","w",stdout);
    int t;
    t = readint();
    int s;
    int ans;
    while (t--)
    {
        s = readint();
        ans = 0;
        while (s)
        {
            if (s > 9)
            {
                ans += s / 10 * 10;
                s = (s % 10) + (s / 10);
            } else
            {
                ans += s;
                s = 0;
            }
        }
        printf("%d\n", ans );
    }
 
    return 0;
}

C. Yet Another Walking Robot

思路:

题目让删除一个长度最小的子串使机器人最后的位置不变,即如果机器人单独执行删除的子串的话,首尾位置不变。

我们设机器人的在接到第i个指令后的位置是\((xi,yi)\),用map查找下之前有没有出现过该位置。

如果有,令j = map[\((xi,yi)\)] ,那么 \([j+1,i]\) 这段子串就符合条件,维护出长度最小的符合条件的子串即可。同时用map记录(或者是更新)下位置\((xi,yi)\) -> i

map是STL中的一个容器,std::map for C++

我的代码中 map存的\((xi,yi)\) 对应的是i+1,这样在维护答案时更方便。

Time complexity: \(O(nlogn)\) per test case.

代码:

int n;
char s[maxn];
map<pii, int> m;
int main()
{
    //freopen("D:\\code\\text\\input.txt","r",stdin);
    //freopen("D:\\code\\text\\output.txt","w",stdout);
    int t;
    t = readint();
    while (t--)
    {
        n = readint();
        m.clear();
        scanf("%s", s + 1);
        int x = 0;
        int y = 0;
        int len = inf;
        int l, r;
        m[mp(0, 0)] = 1;
        repd(i, 1, n)
        {
            if (s[i] == 'L')
            {
                x--;
            } else if (s[i] == 'R')
            {
                x++;
            } else if (s[i] == 'U')
            {
                y++;
            } else
            {
                y--;
            }
            if (m.count(mp(x, y)))
            {
                int j = m[mp(x, y)];
                if (i - j + 1 < len)
                {
                    len = i - j + 1;
                    l = j;
                    r = i;
                }
            }
            m[mp(x, y)] = i+1;
        }
        if (len != inf)
            printf("%d %d\n", l, r);
        else {
            printf("-1\n");
        }
    }
    return 0;
}
 

D. Fight with Monsters

思路:

很明显的,我们可以将每一个h[i]模去(a+b),得到新的h[i],如果它变成了零,让“回滚”的一个轮回。

然后对于每一个怪兽,我们需要耗费\(\lceil\frac{h_i}{a}\rceil - 1\) 次魔法。

\(h[i] =\lceil\frac{h_i}{a}\rceil - 1\) 后,从小到大排个序后贪心取一下即可。

Time complexity: \(O(nlogn)\)

代码:

int n;
ll a, b, k;
ll h[maxn];
ll c;
std::vector<ll> v;
int main()
{
    //freopen("D:\\code\\text\\input.txt","r",stdin);
    //freopen("D:\\code\\text\\output.txt","w",stdout);
    n = readint();
    a = readll(); b = readll(); k = readll();
    c = a + b;
    repd(i, 1, n)
    {
        h[i] = readll();
    }
    int ans = 0;
    repd(i, 1, n)
    {
        h[i] %= c;
        if (h[i] == 0)
            h[i] += c;
        if (h[i] <= a)
        {
            ans++;
        } else
        {
            ll t = h[i] / a;
            if (h[i] % a != 0)
            {
                t++;
            }
            v.push_back(t - 1);
        }
    }
    sort(ALL(v));
    for (auto x : v)
    {
        if (x <= k)
        {
            ans++;
            k -= x;
        }
    }
    printf("%d\n", ans );
    return 0;
}

E1. String Coloring (easy version) /1296E2 - String Coloring (hard version)

思路:

解决本问题应该清楚 狄尔沃斯定理

百科的解释为:

狄尔沃斯定理(Dilworth's theorem)亦称偏序集分解定理,是关于偏序集的极大极小的定理,该定理断言:对于任意有限偏序集,其最大反链中元素的数目必等于最小链划分中链的数目。此定理的对偶形式亦真,它断言:对于任意有限偏序集,其最长链中元素的数目必等于其最小反链划分中反链的数目,由偏序集P按如下方式产生的图G称为偏序集的可比图:G的节点集由P的元素组成,而e为G中的边,仅当e的两端点在P中是可比较的,有限全序集的可比图为完全图

This theorem says that the minimum number of non-decreasing sequences we need to cover the whole sequence equals the length of least decreasing subsequence.

通过分析可发现,

我们从字符串的末尾向左求最长不下降子序列,

用f[i] 代表 第i个位置在最长不下降子序列中的位置。

那么 总体就需要 $max(f[i],i\in[1,n]) $ 个颜色才能满足要求。

第i个字符就染f[i] 色即可。

E1代码:

int n;
char s[maxn];
int ans[maxn];
std::vector<char> v;
int isok = 1;
int main()
{
    //freopen("D:\\code\\text\\input.txt","r",stdin);
    //freopen("D:\\code\\text\\output.txt","w",stdout);
    n = readint();
    scanf("%s", s + 1);
    ans[n] = 0;
    v.push_back(s[n]);
    for (int i = n - 1; i >= 1; --i)
    {
        int pos = lower_bound(ALL(v), s[i]) - v.begin();
        ans[i] = pos;
        if(pos==sz(v))
        {
            v.push_back(s[i]);
        }else
            v[pos] = s[i];
        if (pos > 1)
        {
            isok = 0;
        }
    }
    if (isok)
    {
        printf("YES\n");
        repd(i, 1, n)
        {
            printf("%d", ans[i]   );
        }
        printf("\n");
    } else
    {
        printf("NO\n");
    }
    return 0;
}

E2代码:

int n;
char s[maxn];
int ans[maxn];
std::vector<char> v;
int isok = 1;
int main()
{
    //freopen("D:\\code\\text\\input.txt","r",stdin);
    //freopen("D:\\code\\text\\output.txt","w",stdout);
    n = readint();
    scanf("%s", s + 1);
    ans[n] = 1;
    v.push_back(s[n]);
    int ansnum = 1;
    for (int i = n - 1; i >= 1; --i)
    {
        int pos = lower_bound(ALL(v), s[i]) - v.begin();
        ans[i] = pos + 1;
        if (pos == sz(v))
        {
            v.push_back(s[i]);
        } else
            v[pos] = s[i];
        ansnum = max(ansnum, ans[i]);
    }


    printf("%d\n", ansnum);
    repd(i, 1, n)
    {
        printf("%d%c", ans[i], i == n ? '\n' : ' ' );
    }

    return 0;
}

F. Berland Beauty

思路:

首先把给定的m个限制按照降序排序,

然后降序的对于每一个限制\((ai,bi,gi)\),我们通过dfs 得出 数组\(fa[i]\) 代表 在ai到 bi 的有向路径中 到达节点i的前继节点。然后利用数组fa,更新ai到 bi 路径中边j的边值 \(f_j = max(f_j, g_i)\) ,并用bj 标记下路径中是否有边j ,满足 fj<gi 。如果没有则答案是-1,最后如果各个限制没有冲突,将那些没有更新到的边权设一个[1,1e6] 中的任意值。

代码:

int n;
int id[maxn][maxn];
int fa[maxn];
struct node
{
    int f, t, w;
    bool operator < (const node &b) const
    {
        return w > b.w;
    }
} e[maxn];
int ans[maxn];
int m;
std::vector<int> son[maxn];
 
void dfs(int x, int pre)
{
    for (auto y : son[x])
    {
        if (y != pre)
        {
            fa[y] = x;
            dfs(y, x);
        }
    }
}
int main()
{
    //freopen("D:\\code\\text\\input.txt","r",stdin);
    //freopen("D:\\code\\text\\output.txt","w",stdout);
    n = readint();
    repd(i, 2, n)
    {
        int x, y;
        x = readint();
        y = readint();
        son[x].push_back(y);
        son[y].push_back(x);
        id[x][y] = id[y][x] = i;
    }
    m = readint();
    repd(i, 1, m)
    {
        e[i].f = readint();
        e[i].t = readint();
        e[i].w = readint();
    }
    sort(e + 1, e + 1 + m);
    int flag = 1;
 
    repd(i, 1, m)
    {
        dfs(e[i].f, 0);
        int now = e[i].t;
        int bj = 0;
        do
        {
            int j = id[now][fa[now]];
            if (ans[j] <= e[i].w)
            {
                bj = 1;
                ans[j] = e[i].w;
            }
            now = fa[now];
        } while (now != e[i].f);
        if (!bj)
        {
            flag = 0;
            break;
        }
    }
    if (flag)
    {
        repd(i, 2, n)
        {
            printf("%d%c", max(ans[i], 1), i == n ? '\n' : ' ');
        }
    } else
    {
        printf("-1\n");
    }
    return 0;
}