Educational Codeforces Round 122 (Rated for Div. 2)/codeforces1633

CodeForces1633

Div. 7

解析:

题目大意

给定 \(t\) 组数据。每组数据给定一个数 \(n\)\(10\le n\le 999\))。

每次操作可以修改 \(n\) 任意一位上的数,将这一位上的数修改为 \(0\sim 9\) 之间的任意数。要求使用最少的修改次数使这个数修改后是 \(7\) 的倍数,并且没有多余的前导 \(0\)。输出修改后的数,如果有多组解,输出任意一种即可。如果已经是 \(7\) 的倍数,那么不需要修改。


思路:

发现修改个位一定能让 \(n\) 能被 \(7\) 整除,直接枚举个位换哪个数。


code

#include <bits/stdc++.h>
using namespace std;
signed main()
{
    int t; scanf ("%d", &t);
    while (t--)
    {
        int n; scanf ("%d", &n);
        if (n % 7 == 0) printf ("%d\n", n);
        else
        {
            n = n / 10 * 10;
            for (int i = n; i <= n + 10; i++)
                if (i % 7 == 0) { printf ("%d\n", i); break; }
        }
    }
    return 0;
}

Minority

解析:

题目大意:
  • 给定一个 \(01\) 字符串 \(s\),定义 \(c_k(l,r)\) 表示 \(s\) 的由下标为 \([l,r]\) 中的字母构成的连续子串中 \(k\) 的个数。
  • 定义 \(f(l,r)=\begin{cases}c_0(l,r)&c_0(l,r)<c_1(l,r)\\c_1(l,r)&c_0(l,r)>c_1(l,r)\\0&c_0(l,r)=c_1(l,r)\end{cases}\),求 \(\max\limits_{1\le l\le r\le n}f(l,r)\)。多组询问。
  • \(T\le10^4,\sum|s|\le2\times10^5\)

思路:

诈骗题,\(f(1,n)\) 一定最大,因为这个区间含有的 \(0,1\) 都最多,如果 \(c_0(l,r)=c_1(l,r)\) 那个答案就是 \(c_0(l,r)-1\)(取 \([1,n-1]\))。


code:

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
char ch[N];
int cnt1, cnt2;
signed main()
{
    int t; scanf ("%d", &t);
    while (t--)
    {
        cnt1 = cnt2 = 0;
        scanf ("%s", ch + 1);
        int n = strlen (ch + 1);
        if (n <= 2) { printf ("0\n"); continue; }
        for (int i = 1; i <= n; i++)
        {
            if (ch[i] == '0') cnt1++;
            else cnt2++;
        }
        if (cnt1 != cnt2) printf ("%d\n", min(cnt1, cnt2));
        else printf ("%d\n", cnt1 - 1);
    }
    return 0;
}

Kill the Monster

解析:

题目大意

在某款电脑游戏里,角色初始生命 \(hc\),初始攻击 \(dc\)。怪物初始生命 \(hm\),初始攻击 \(dm\)

而你有 \(k\) 次机会,将攻击力永久提升 \(w\) 或者将生命永久提升 \(a\)

每一轮,角色攻击怪物,怪物掉 \(dc\) 生命。怪物攻击角色,角色掉 \(dm\) 生命。

问若干轮以后,角色能否击杀怪物并且角色存活。

击杀怪物定义为:怪物生命 \(\ngtr 0\)

角色存活定义为:角色生命 \(>0\)


思路:

枚举 \(0\to k\) 分别给攻击和生命多少次机会,如果某一时刻 $ \lceil\frac{hc}{dm}\rceil \ge \lceil\frac{hm}{dc}\rceil$ 那么就可以胜利。


code

#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read ()
{
    int x = 0, f = 1;
    char ch = getchar ();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
    while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
    return x * f;
}
signed main()
{
    int t = read ();
    while (t--)
    {
        int hc = read (), dc = read ();
        int hm = read (), dm = read ();
        int k = read (), w = read (), a = read ();
        bool flag = false;
        for (int i = 0; i <= k; i++)
        {
            int h = hc + (i * a), d = dc + ((k - i) * w);
            if ((hm + d - 1) / d <= (h + dm - 1) / dm) { printf ("YES\n"); flag = true; break; }
        }
        if (!flag) printf ("NO\n");
    }
    return 0;
}

Make Them Equal

解析:

题目大意:

你有一个长度为 \(n\),初始全为 \(1\) 的数组 \(a\),和两个长度为 \(n\) 的数组 \(b,c\)

你可以最多进行 \(k\) 次如下的操作:选择两个正整数 \(i,x\),使 \(a_{i}\) 变成 $ \left ( a_{i}+\left \lfloor \dfrac{a_{i}}{x} \right \rfloor \right )$。

最后,如果 \(a_{i}=b_{i}\),你将会获得 \(c_{i}\) 的收益。

最大化总收益。

  • \(1 \leq t \leq 100,1 \leq n \leq 1000, 1 \leq k \leq 1 \times 10^{6}\)
  • \(1 \leq b_{i} \leq 1000,1 \leq c_{i} \leq 1 \times 10^{6}\)
  • \(\sum n \leq1000\)

思路:

我们注意到 \(a_i\) 的初始值都是 \(1\),而 \(b_i\leq 1000\) 所以我们可以预处理出来每个值由 \(1\) 得到的最小次数,这部分预处理复杂度是 \(\mathcal O(V^2)\) 的。现在问题转化成了:

  • 最多操作 \(k\) 次,还原第 \(i\) 个需要操作 \(cnt_{b_i}\) 次,可以得到 \(c_i\) 的价值,问最大价值。

发现问题变成了 01背包问题,复杂度 \(\mathcal O(nk)\),可能会被卡常,注意 \(k\geq \sum cnt_{b_i}\) 可以剪枝。


code:

#include <bits/stdc++.h>
using namespace std;
const int N = 1000 + 10;
const int K = 1e6 + 10;
typedef pair <int, int> pii;
inline int read ()
{
    int x = 0, f = 1;
    char ch = getchar ();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
    while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
    return x * f;
}
int n, k;
int b[N], c[N], cnt[N * 2];
int dp[K];
void init ()
{
    memset (cnt, 0x3f, sizeof (cnt));
    cnt[1] = 0;
    for (int i = 1; i <= 1000; i++)
        for (int j = 1; j <= i; j++)
            cnt[i + i / j] = min (cnt[i + i / j], cnt[i] + 1);
}
signed main()
{
    int t = read ();
    init ();
    while (t--)
    {
        n = read (), k = read (); int sum = 0, s = 0;
        for (int i = 1; i <= n; i++) b[i] = read (), sum += cnt[b[i]];
        for (int i = 1; i <= n; i++) c[i] = read (), s += c[i];
        if (k >= sum) { printf ("%d\n", s); continue; }
        memset (dp, 0, sizeof (dp));
        for (int i = 1; i <= n; i++)
            for (int j = k; j >= cnt[b[i]]; j--)
                dp[j] = max (dp[j], dp[j - cnt[b[i]]] + c[i]);
        printf ("%d\n", dp[k]);
    }
    return 0;
}

Spanning Tree Queries

解析:

题目大意:

给定一个包含 \(n\) 个点和 \(m\) 条边的无向带权图,你有 \(k\) 次询问,第 \(i\) 次询问给定一个整数 \(q_i\),你需要从图中选出一棵生成树,设该生成树的 \(n-1\) 条边的权值为 \(w_1,w_2,\dots,w_{n-1}\),你需要求出 \(\sum\limits_{j=1}^{n-1}|w_j-q_i|\) 的最小值。

该题所有 \(k\) 次询问中,前 \(p\) 次询问的 \(q_1,q_2,\dots,q_p\) 在输入中给定,从第 \(p+1\) 次询问开始的 \(q_i=(q_{i-1}\cdot a+b)\bmod c\)

  • \(2\leqslant n\leqslant 50\)\(n-1\leqslant m\leqslant 300\)\(1\leqslant p\leqslant 10^5\)\(p\leqslant k\leqslant 10^7\)\(0\leqslant a,b\leqslant 10^8\)\(1\leqslant c\leqslant 10^8\)
  • \(0\leqslant w_i\leqslant 10^8\)

思路:

发现这道题的 \(k\leq 10^7\),也就是我们必须在 \(\log\) 的时间内处理每个答案,这样 \(\mathcal O(km\log m)\)\(k\) 次 Kruskal 肯定不行,我们需要考虑预处理答案。

而绝对值有好的性质,我们按照边权排序后,正常 Kruskal 是从头到尾扫,而绝对值则要求先扫在数轴上更靠近 \(q_i\) 的边,我们可以二分找到 \(q_i\) 在边权 \(e_i\) 中的具体位置,并将边权分为两类:小于 \(q_i\) 和大于 \(q_i\) 的。

假设 \(e_1\le e_2\le\cdots\le e_j\le q_i\le e_{j+1} \le \cdots \le e_m\)

则必有 \(|e_1-q_i| \ge |e_2-q_i| \ge \cdots \ge |e_j-q_i|\)\(|e_{j+1}-q_i| \le|e_{j+2}-q_i| \le \cdots \le |e_m-q_i|\),我们可以开两个队列分别从 \(j\to 1\)\(j+1\to m\) 维护每条边权,每次取两个队列中与 \(q_i\) 绝对值的较小值,这样就达到了排序的目的。

同时,我们注意到 \(m\leq 300\),也就是我们可以对每个 \(j\) 分别考虑,考虑每个 \(e_j\leq q\leq e_{j+1}\) 时的生成树情况,但对于即使对于同一个 \(j\),不同的 \(q\) 所对应的生成树也是可能不同的,即在两个队列中会造成不同的选数顺序,我们考虑这样的情况最多有 \(\begin{pmatrix} m \\ 2 \\ \end{pmatrix}\) 种不同的可能(在 \(m\) 条边中任选两条边取平均值),这样最终不同生成树的个数是 \(\mathcal O(m)\cdot\mathcal O(m^2)=\mathcal O(m^3)\) 种。

时间复杂度 \(\mathcal O(m^3\log m+k\log m^2)\)


code:

#include <bits/stdc++.h>
#define int long long
#define eb emplace_back
#define lob lower_bound
using namespace std;
const int N = 300 + 10;
const int SN = 1e5 + 10;
const int INF = 0x3f3f3f3f;
typedef pair <int, int> pii;
inline int read ()
{
    int x = 0, f = 1;
    char ch = getchar ();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
    while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
    return x * f;
}
int n, m;
int p, k, a, b, c;
struct edge {
    int u, v, w;
    bool operator < (const edge &A)const {
        return w < A.w;
    }
}e[N];
int g[SN], cnt;
int ans[SN], cntA[SN][2];
queue <int> que[2];
struct DSU {
    int fa[N]; 
    void init () { for (int i = 1; i <= n; i++) fa[i] = i; }
    inline int find (int x) { return x == fa[x] ? x : fa[x] = find (fa[x]); }
} d;
void clear (int k) { while (!que[k].empty ()) que[k].pop (); }
void kruskal (int id, int x)
{
    d.init ();
    clear (0); clear (1);
    int pos = lob (e + 1, e + m + 1, (edge) {0, 0, x}) - e;
    for (int i = pos - 1; i >= 1; i--) que[0].push (i);
    for (int i = pos; i <= m; i++) que[1].push (i);
    while (!que[0].empty () || !que[1].empty ())
    {
        int c, cid;
        if (que[0].empty ()) c = 1, cid = que[1].front ();
        else if (que[1].empty ()) c = 0, cid = que[0].front ();
        else
        {
            if (abs (x - e[que[0].front ()].w) <= abs (x - e[que[1].front ()].w)) c = 0, cid = que[0].front ();
            else c = 1, cid = que[1].front ();
        }
        int fx = d.find (e[cid].u), fy = d.find (e[cid].v);
        if (fx != fy)
        {
            d.fa[fx] = fy;
            ans[id] += abs (x - e[cid].w);
            cntA[id][c]++;
        }
        que[c].pop ();
    }
}
int Xor;
inline int getans (int q)
{
    int x = lob (g + 1, g + cnt + 1, q) - g;
    return ans[x] - cntA[x][0] * (g[x] - q) + cntA[x][1] * (g[x] - q);
}
signed main()
{
    n = read (), m = read ();
    for (int i = 1; i <= m; i++)
        e[i].u = read (), e[i].v = read (), e[i].w = read ();
    sort (e + 1, e + m + 1);
    for (int i = 1; i <= m; i++)
        for (int j = 1; j <= m; j++)
            g[++cnt] = (e[i].w + e[j].w) >> 1;
    g[++cnt] = INF;
    sort (g + 1, g + cnt + 1);
    cnt = unique (g + 1, g + cnt + 1) - g - 1;
    for (int i = 1; i <= cnt; i++) kruskal (i, g[i]);
    p = read (), k = read (), a = read (), b = read (), c = read ();
    int q;
    for (int i = 1; i <= p; i++) { q = read (); Xor ^= getans(q); }
    for (int i = p + 1; i <= k; i++) { q = (q * a + b) % c; Xor ^= getans(q); }
    printf ("%lld\n", Xor);
    return 0;
}

6

解析:

题目大意:


思路:

没读题,先咕了


code:


posted @ 2022-08-04 11:56  TheDarkEmperor  阅读(28)  评论(0编辑  收藏  举报