Featured image of post Codeforces Round #1080(Div.3)

Codeforces Round #1080(Div.3)

这次除了H构造,确实都很trivial啊,G的思路不难但是写得少~

为什么AB会WA呢,还是不够稳。

D

题目大意:现在有隐藏序列 $a_1,a_2,\ldots,a_n$ ,保证 $|a_i| \leq 1000$ ,现在给定 $p_1,p_2,\ldots,p_n,p_i=\sum\limits_{i=1}^{n}a_i \cdot |i-x|$ ,要求还原原来的 $a_i$ 。

数据范围: $2 \leq n \leq 3 \cdot 10^5, -10^{14} \leq p_i \leq 10^{14}$

思路:把式子写出来,然后拆项加和差分啥的,简单的数学直觉,有群友跑去高斯消元还做不出来,我不好评价。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
void solve() {
    int n;
    cin >> n;
    vl f(n);
    vl a(n);
    rep(i, 0, n - 1) cin >> f[i];
    ll tot = (f[0] + f[n - 1]) / (n - 1);
    ll tem = 0;
    rep(i, 0, n - 2) {
        a[i] = (tot - (f[i] - f[i + 1] + 2LL * tem)) / 2;
        tem += a[i];
    }
    a[n - 1] = tot;
    rep(i, 0, n - 2) a[n - 1] -= a[i];
    rep(i, 0, n - 1) cout << a[i] << ' ';
    cout << endl;
    return;
}

E

题目大意:有 $n+1$ 个顶点的二叉树,每个顶点上最多可以写一个字母,所有顶点上最初啥都不写,根为0,0直接跟1连着,除0外不存在只有一个子节点的节点,现在Bob发明了白痴优先搜索,规则如下,经验证可以让他最终走到0,请给出每个节点走到0的步数,并模 $10^9+7$ 。

规则:如果当前节点是叶子,那么移动到父节点,否则,若顶点为空,则写入 $L$ ,并移动到左顶点,若为 $L$ ,则覆盖为 $R$ ,并移动到右顶点,若为 $R$ 则删掉并移动到父节点。

数据范围: $1 \leq n \leq 3 \cdot 10^5 +1$

思路:手玩一下样例,发现由于走到父节点后父节点还会再下来走一次,而且题目的形式非常像换根,因此考虑自上而下换根DP。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const int MOD = 1e9 + 7;
void solve() {
    int n;
    cin >> n;
    vector<pii> ma(n + 1);
    rep(i, 1, n) cin >> ma[i].first >> ma[i].second;
    vector<pll> siz(n + 1);
    vl res(n + 1);
    auto dfs = [&](this auto&& dfs, int x) -> int {
        if (ma[x].first != 0) siz[x].first = dfs(ma[x].first);
        if (ma[x].second != 0) siz[x].second = dfs(ma[x].second);
        return siz[x].first + siz[x].second + 1;
    };
    dfs(1);
    res[1] = (2LL * siz[1].first + 2LL * siz[1].second + 1) % MOD;
    auto dfs2 = [&](this auto&& dfs2, int x, int pa) -> void {
        if (x != 1) {
            res[x] = (res[pa] + 2LL * siz[x].first + 2LL * siz[x].second + 1) % MOD;
        }
        if (ma[x].first) dfs2(ma[x].first, x);
        if (ma[x].second) dfs2(ma[x].second, x);
        return;
    };
    dfs2(1, -1);
    rep(i, 1, n) cout << res[i] << ' ';
    cout << endl;
    return;
}

F

题目大意:给定 $n$ 个二次函数,其中 $f_i=a_i x^2+b_i x + c_i$ ,如果两个函数没有交点,那么称他们为独立函数,如果某个集合中函数两两独立,那么称为独立集,请求出最大独立集大小。

数据范围: $1 \leq n \leq 3000, -10^6 \leq a_i,b_i,c_i \leq 10^6$

思路:考虑不交的函数有什么性质,判别式不为0?(或者注意二次项系数一样时的情况),由零点存在性原理,是恒大于或者恒小于,那么这里就存在偏序关系了,包含某个点的最大集合,就是拓扑排序中最长链的长度(因为显然地,这里存在一个有向无环图),按照拓扑序DP即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
void solve() {
    int n, a, b, c;
    cin >> n;
    vector<tuple<ll, ll, ll>> ma(n);
    rep(i, 0, n - 1) {
        cin >> a >> b >> c;
        ma[i] = make_tuple(a, b, c);
    }
    vi res(n);
    vvi ma2(n);
    vvi ma3(n);
    vi deg(n);
    vi deg2(n);
    rep(i, 0, n - 1) {
        rep(j, i + 1, n - 1) {
            ll a1 = get<0>(ma[i]), a2 = get<0>(ma[j]);
            ll b1 = get<1>(ma[i]), b2 = get<1>(ma[j]);
            ll c1 = get<2>(ma[i]), c2 = get<2>(ma[j]);
            if (a1 != a2 && (b1 - b2) * (b1 - b2) - 4 * (a1 - a2) * (c1 - c2) < 0) {
                if (a1 > a2) {
                    ma2[i].push_back(j);
                    ma3[j].push_back(i);
                    deg[j]++;
                    deg2[i]++;
                } else {
                    ma2[j].push_back(i);
                    ma3[i].push_back(j);
                    deg[i]++;
                    deg2[j]++;
                }

            } else if (a1 == a2 && b1 == b2 && c1 != c2) {
                if (c1 > c2) {
                    ma2[i].push_back(j);
                    ma3[j].push_back(i);
                    deg[j]++;
                    deg2[i]++;
                } else {
                    ma2[j].push_back(i);
                    ma3[i].push_back(j);
                    deg[i]++;
                    deg2[j]++;
                }
            }
        }
    }
    vi dp(n);
    vi fdp(n);
    auto bfs = [&](this auto&& bfs, vvi& ma, vi& deg, vi& dp) -> void {
        queue<int> q;
        rep(i, 0, n - 1) {
            if (deg[i] == 0) q.push(i);
        }
        while (!q.empty()) {
            auto node = q.front();
            q.pop();
            for (int& p : ma[node]) {
                dp[p] = max(dp[p], dp[node] + 1);
                if (--deg[p] == 0) {
                    q.push(p);
                }
            }
        }
    };
    bfs(ma2, deg, dp);
    bfs(ma3, deg2, fdp);
    rep(i, 0, n - 1) cout << dp[i] + fdp[i] + 1 << ' ';
    cout << endl;
}

G

题目大意:跟E题一样,但是此时给出 $q$ 个询问,每个询问需要回答最开始位置为 $x$ ,走了 $k$ 步后的位置。

数据范围: $1 \leq n \leq 3 \cdot 10^5+1, 1 \leq q \leq 4 \cdot 10^5, 1 \leq v_j \leq n, 0 \leq k_j \leq \mathrm{min}(10^9+7,T_{v_j})$

思路:既然是数数题,那么很显然地,需要使用树上倍增,赛时能一眼看破这个,但是不大会写,补的时候参照了一下前排大佬的代码。

同时,注意到这里遍历的顺序比较奇怪,也就是说刚好是欧拉序(欧拉序:每次到一个节点就记录它,子树走完再回来),如果跳到某个祖先还有剩下的步数,那么使用欧拉序求解。

最后,需要使用数数来计数向上跳的步数,这里注意到应该自下而上先统计一个节点走到它的父节点所需步数,再在倍增的时候直接跳,需要注意的是, $up$ 和 $st$ 两个数组的含义不同,写的时候需要辨别。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
const int MOD = 1e9 + 7;
const ll INF = (1LL << 50);
void solve() {
    int n, q, v, k;
    cin >> n >> q;
    vector<pii> ma(n + 1);
    vi pa(n + 1);
    vl dp(n + 1);
    vvi up(n + 1, vi(20));
    vvl st(n + 1, vl(20, INF));
    pa[1] = 0;
    rep(i, 1, n) {
        cin >> ma[i].first >> ma[i].second;
        if (ma[i].first != 0) pa[ma[i].first] = i;
        if (ma[i].second != 0) pa[ma[i].second] = i;
    }
    vi path;
    vector<pll> siz(n + 1);
    vi pos(n + 1);
    auto dfs = [&](this auto&& dfs, int x) -> int {
        pos[x] = sz(path);
        path.push_back(x);
        if (ma[x].first != 0) {
            siz[x].first = dfs(ma[x].first);
            dp[x] += dp[ma[x].first] + 1;
            path.push_back(x);
            siz[x].second = dfs(ma[x].second);
            dp[x] += dp[ma[x].second] + 1;
            path.push_back(x);
        }
        dp[x]++;
        return siz[x].first + siz[x].second + 1;
    };
    dfs(1);
    path.push_back(0);
    rep(i, 0, n) up[i][0] = pa[i], st[i][0] = dp[i];
    rep(j, 0, 18) {
        rep(i, 1, n) {
            int mid = up[i][j];
            if (mid) {
                up[i][j + 1] = up[mid][j];
                if (st[i][j] != INF && st[mid][j] != INF) st[i][j + 1] = st[mid][j] + st[i][j];
            }
        }
    }
    rep(i, 0, q - 1) {
        cin >> v >> k;
        frep(j, 19, 0) {
            if (up[v][j] && st[v][j] <= k) {
                k -= st[v][j];
                v = up[v][j];
            }
        }
        cout << path[pos[v] + k] << ' ';
    }
    cout << endl;
    return;
}