Codeforces Round #601 (Div. 1)

Codeforces Round #601 (Div. 1)

A

容易发现最小的差肯定是 (1),即有一些块包含 (x) 个,一些块有 (x+1) 个。关键在于保证连通。只要按照“蛇形”拓展即可:一行一行走,第一行从左往右,第二行从右往左,第三行...

#include <bits/stdc++.h>
using namespace std;
void read (int &x) {
    char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 105;
int n, m, k, p[100], vis[N][N]; char a[N][N], b[N][N];
signed main() {
    int T; read (T); int m = 0;
    for (int i = '0'; i <= '9'; ++i) p[++m] = i;
    for (int i = 'a'; i <= 'z'; ++i) p[++m] = i;
    for (int i = 'A'; i <= 'Z'; ++i) p[++m] = i;
    while (T--) {
        read (n), read (m), read (k);
        for (int i = 1; i <= n; ++i) scanf ("%s", a[i] + 1);
        int cnt = 0;
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j) cnt += a[i][j] == 'R', vis[i][j] = 0;
        int num = cnt / k, s = cnt - k * num;
        int x = 1, y = 1, now = 0, f = 1, id = 1;
        while (id <= s) {
            // printf ("%d %d %d
", x, y, id);
            now += a[x][y] == 'R', b[x][y] = p[id], vis[x][y] = 1;
            if (y + f <= m && y + f >= 1) y += f;
            else ++x, f = -f; if (x > n) break;
            if (now == num + 1) now = 0, ++id;
        }
        while (id <= k) {
            // printf ("%d %d %d
", x, y, id);
            now += a[x][y] == 'R', b[x][y] = p[id], vis[x][y] = 1;
            if (y + f <= m && y + f >= 1) y += f;
            else ++x, f = -f; if (x > n) break;
            if (now == num) now = 0, ++id;
        }
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j) if (!vis[i][j]) b[i][j] = p[id - 1];
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) putchar (b[i][j]); putchar ('
');
        }
    }
    return 0;
}

B

对于 easy version,枚举约数 (p),由于每个位置不超过 (1),一定是连续 (p)(1) 拼在一起。然后就成了“数轴上有一些村庄,建车站使距离和最短”的问题

对于 hard,首先可以发现 (p) 只需考虑质数,否则不会更优。为了简化问题,设 (b_i=sumlimits_{j=1}^{i}a_i)。把 (i) 移到 (i+1) 就是 (b_i)(1),移到 (i-1) 就是 (b_{i-1})(1)。其实就是可以任选一个 (iin[1,n-1]) 加一减一。(res=sum min(b_i\%p,p-b_i\%p))

质因数没几个((leq12)),所以很快

#include <bits/stdc++.h>
using namespace std;
#define int long long
void read (int &x) {
    char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 1e6 + 5;
int n, a[N], s[N], p[N], in[N];
signed main() {
    read (n);
    for (int i = 1; i <= n; ++i) read (a[i]);
    for (int i = 1; i <= n; ++i) s[i] = s[i - 1] + a[i];
    if (s[n] == 1) return puts ("-1"), 0;
    int res = 5e18, sum = s[n];
    for (int i = 2; i * i <= s[n]; ++i) {
        if (!in[i]) {
            if (sum % i == 0) {
                while (sum % i == 0) sum /= i; int tmp = 0;
                for (int j = 1; j <= n; ++j) tmp += min (s[j] % i, i - s[j] % i);
                res = min (res, tmp);
            }
            for (int j = i; j * j <= s[n]; j += i) in[j] = 1;
        }
    }
    if (sum > 1) {
        int tmp = 0;
        for (int j = 1; j <= n; ++j) tmp += min (s[j] % sum, sum - s[j] % sum);
        res = min (res, tmp);
    }
    return printf ("%lld
", res), 0;
}

C

先通过 (1,2) 两点确定的直线把点分成 (A,B) 两部分,这一步可以通过 (n-2)(2) 类询问 ((1,2,x)) 完成

然后对于每个点进行 (1) 类查询 ((1,2,x)),由此可以得出每个点到直线 (1,2) 的距离。记最远点为 (u),再通过 (2) 类询问 ((1,u,x)) 可以把 (A) 中的点再次分为 (A_1,A_2) 两部分 ((B) 同)

然后对于 (A_1,A_2) 点的顺序分别按照点到线的距离降序、升序排列。需要注意的是最远点可能有两个

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1024;
int n, h[N]; vector<int> a, b, res;
void flush () { putchar ('
'); fflush (stdout); }
vector<pair<int, int> > l, r;
#define fi first
#define se second
#define pb push_back
signed main() {
    cin >> n;
    for (int i = 3, x; i <= n; ++i) {
        printf ("2 1 2 %lld", i);
        flush (); cin >> x;
        x == -1 ? a.pb (i) : b.pb (i);
    }
    int u = 0, d = 0, v = 0;
    for (int i : a) {
        printf ("1 1 2 %lld", i);
        flush (); int x; cin >> x; h[i] = x;
        if (x > d) d = x, u = i, v = 0;
        else if (x == d) v = i;
    }
    for (int i : a) {
        if (i == u || i == v) continue;
        printf ("2 1 %lld %lld", u, i);
        flush (); int x; cin >> x;
        if (x == 1) r.pb ({h[i], i});
        else l.pb ({h[i], i});
    }
    res.pb (1);
    sort (l.begin(), l.end());
    for (auto i : l) res.pb (i.se);
    if (!v && u) res.pb (u);
    if (u && v) {
        printf ("2 1 %lld %lld", u, v);
        flush (); int x; cin >> x;
        if (x < 0) swap (u, v);
        res.pb (u), res.pb (v);
    }
    sort (r.begin(), r.end());
    reverse (r.begin(), r.end());
    for (auto i : r) res.pb (i.se);
    res.pb (2);
    ////////////////////////////
    u = 0, d = 0, v = 0; l.clear(), r.clear();
    for (int i : b) {
        printf ("1 1 2 %lld", i);
        flush (); int x; cin >> x; h[i] = x;
        if (x > d) d = x, u = i, v = 0;
        else if (x == d) v = i;
    }
    for (int i : b) {
        if (i == u || i == v) continue;
        printf ("2 1 %lld %lld", u, i);
        flush (); int x; cin >> x;
        if (x == 1) l.pb ({h[i], i});
        else r.pb ({h[i], i});
    }
    sort (r.begin(), r.end());
    for (auto i : r) res.pb (i.se);
    if (!v && u) res.pb (u);
    if (u && v){
        printf ("2 1 %lld %lld", u, v);
        flush (); int x; cin >> x;
        if (x < 0) swap (u, v);
        res.pb (u), res.pb (v);
    }
    sort (l.begin(), l.end());
    reverse (l.begin(), l.end());
    for (auto i : l) res.pb (i.se);
    printf ("0 ");
    for (int i : res) printf ("%lld ", i);
}

D

操作 (x),此时以 (x) 为根,(y) 被修改的概率和 (y)(x) 的哪个儿子的子树中有关,只要 (r) 不选在这棵子树中 (y) 就被修改。容易发现朴素算法的复杂度是操作节点的度数之和

1、对于度数分类处理,(gesqrt{n}) 的打标记,查询的时候再算,(leqsqrt{n}) 的直接暴力修改,根据实现方式不同可以做到 (O(nsqrt{n})——O(nsqrt{n}log{n}))

2、轻重链剖分,对于 (x) 的重儿子的子树和外子树暴力修改,轻儿子不管,查询的时候跳重链累计答案(两条重链相接处)。每次暴力修改 (2) 个区间,(O(log{n}))。每次查询的时候重链最多 (log n) 条,概率 (O(1)) 计算,复杂度也为 (O(log n))。总复杂度 (O(nlog n))

#include <bits/stdc++.h>
using namespace std;
#define int long long
void read (int &x) {
    char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 150010, M = N << 1, mod = 998244353;
int qpow (int x, int y) {
    int t = 1;
    while (y) {
        if (y & 1) t = t * x % mod;
        x = x * x % mod, y >>= 1;
    }
    return t;
}
int n, q, cnt, inv, h[N], to[M], nxt[M];
void add (int u, int v) {
    to[++cnt] = v, nxt[cnt] = h[u], h[u] = cnt;
}
#define up(x, y) ((x += y) >= mod && (x -= mod))
struct tree {
    int c[N];
    void add (int x, int v) {
        for (++x; x <= n + 1; x += (x & -x)) up (c[x], v);
    }
    int ask (int x) {
        int s = 0; for (++x; x; x -= (x & -x)) up (s, c[x]); return s;
    }
    void update (int l, int r, int v) { add (l, v), add (r + 1, mod - v); }
} t;
struct solve {
    int cnt, d[N], son[N], sz[N], dfn[N], fa[N], tp[N], ed[N], num[N];
    void dfs (int u) {
        sz[u] = 1;
        for (int i = h[u], v; i; i = nxt[i]) {
            if ((v = to[i]) == fa[u]) continue;
            fa[v] = u, d[v] = d[u] + 1, dfs (v), sz[u] += sz[v];
            if (sz[v] > sz[son[u]]) son[u] = v;
        }
    }
    void dfs (int u, int topp) {
        tp[u] = topp, dfn[u] = ++cnt;
        if (son[u]) dfs (son[u], topp);
        for (int i = h[u], v; i; i = nxt[i])
            if (!dfn[v = to[i]]) dfs (v, v); ed[u] = cnt;
    }
    void prework () { dfs (1); dfs (1, 1); }
    void modify (int x, int c) {
        int l = 1, r = dfn[x] - 1, k = c * inv % mod * sz[x] % mod;
        if (l <= r) t.update (l, r, k);
        l = ed[x] + 1, r = n;
        if (l <= r) t.update (l, r, k);
        if (son[x]) {
            int y = son[x];
            l = dfn[y], r = ed[y], k = c * inv % mod * (n - sz[y]) % mod;
            if (l <= r) t.update (l, r, k);
        }
        up (num[x], c);
    }
    int ask (int x) {
        int res = num[x]; up (res, t.ask (dfn[x]));
        while (x) {
            int a = tp[x], b = fa[tp[x]]; x = b;
            (res += num[b] * inv % mod * (n - sz[a])) %= mod;
        }
        return res;
    }
} qwq;
signed main() {
    read (n), read (q); inv = qpow (n, mod - 2);
    for (int i = 1, u, v; i < n; ++i)
        read (u), read (v), add (u, v), add (v, u);
    qwq.prework (); int o, x, c;
    for (int i = 1; i <= q; ++i) {
        read (o), read (x);
        if (o == 1) read (c), qwq.modify (x, c);
        else printf ("%lld
", qwq.ask (x));
    }
    return 0;
}

E

如果没有限制,(res=prod s_i)(s_i)(i) 节点的度数。

一个限制意味着什么?提取出 (path(x,a_x)) 这条链,有三种限制

1、(x) 的第一条删除的边固定。

2、(a_x) 的最后一条删除边固定。

3、链上的边删除有依赖关系(可传递)。即删除完一条边后,和公共点相连的边中必须立刻删除链上的下一条。

注意到有依赖关系的边都有一个公共点。

把和某个点相邻的边提出来,把这些边看成节点,每个依赖关系连一条有向边,构成一张“新图”(下面提到“新链”是“新图”中的链,“链”指原图的链)

可以找到一些无解的情况:

1、某个 (x)(a) 中出现不止一次

2、和某条边有直接依赖关系的边不止一条或依赖关系成环(In other words,“新图”中有节点出度或入度 (ge 2) 或 "新图"中有环)

3、某个点的第一条边和最后一条边固定且在同一条“新链”上,但还有其他“新链”存在

4、如果链 (path(x,a_x)) 的长度之和 (ge 2n-1),一定无解,因为每删除一条边 (i) 节点和数字 (i) 的距离之和 (+2)。所以可以暴力判断无解而保证复杂度。

如果有解,答案就是 (prod c_i!)(c_i) 为“新链”数量。

语文水平太低,说的很模糊,但又懒得画图,不如↓

#include <bits/stdc++.h>
using namespace std;
#define int long long
void read (int &x) {
    char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 5e5 + 5, mod = 1e9 + 7;
int n; vector<int> g[N];
#define int long long
void fail () { puts ("0"); exit (0); }
int sum, d[N], fa[N], nxt[N], pre[N], vis[N];
void dfs (int u, int la) {
    d[u] = d[la] + 1, fa[u] = la;
    for (int v : g[u]) if (v != la) dfs (v, u);
}
vector<int> vx, vy;
#define pb push_back
void lca (int x, int y) {
    vx.clear(), vy.clear(); vx.pb (n + 1), vy.pb (n + 2);
    while (d[x] > d[y]) ++sum, vx.pb (x), x = fa[x];
    while (d[x] < d[y]) ++sum, vy.pb (y), y = fa[y];
    while (x != y) sum += 2, vx.pb (x), vy.pb (y), x = fa[x], y = fa[y];
    vx.pb (x);
}
struct e { int x, y; } ; vector<e> a[N];
void work (int x, int y) {
    lca (x, y); if (sum > 2 * n - 2) fail ();
    vx.insert (vx.end(), vy.rbegin(), vy.rend()); int sx = (int)vx.size();
    for (int i = 1; i + 1 < sx; ++i) a[vx[i]].pb ({vx[i - 1], vx[i + 1]});
}
signed main() {
    read (n);
    for (int i = 1, u, v; i < n; ++i)
        read (u), read (v), g[u].pb (v), g[v].pb (u);
    for (int i = 1; i <= n; ++i)
        g[i].pb (n + 1), g[i].pb (n + 2);
    if (n == 1) return puts ("1"), 0;
    dfs (1, 1); int res = 1;
    for (int i = 1, x; i <= n; ++i) { read (x); if (x) work (i, x); }
    for (int i = 1; i <= n; ++i) {
        for (e p : a[i]) {
            int x = p.x, y = p.y;
            if (nxt[x] && nxt[x] != y) fail ();
            if (pre[y] && pre[y] != x) fail ();
            nxt[x] = y, pre[y] = x;
        }
        for (int x : g[i]) if (!vis[x]) {
            vis[x] = 1; int now = nxt[x];
            while (now) {
                if (now == x) fail ();
                if (vis[now]) break; vis[now] = 1, now = nxt[now];
            }
        }
        if (nxt[n + 1]) {
            int now = n + 1, c = 1;
            while (now) {
                if (now == n + 2) break; ++c, now = nxt[now];
            }
            if (now == n + 2 && c < g[i].size()) fail ();
        }
        int num = 0;
        for (int x : g[i]) num += (x <= n && !pre[x]);
        if (pre[n + 2]) --num;
        for (int j = 1; j <= num; ++j) res = res * j % mod;
        for (int x : g[i]) nxt[x] = pre[x] = vis[x] = 0;
    }
    printf ("%lld
", res);
}

原文地址:https://www.cnblogs.com/whx666/p/601-div1.html