其他分享
首页 > 其他分享> > Codeforces Round #609 (Div. 1)

Codeforces Round #609 (Div. 1)

作者:互联网

Codeforces Round #609 (Div. 1)

A

先保留前 \(k\) 位写出当前的答案。如果合法直接输,如果不合法把前 \(k\) 位数字 \(+1\) 重新写

B

将网格图黑白染色,答案是 \(cnt=min(num(black),num(white))\)。可通过二分图匹配或手动构造证明

C

考虑分成两步,1.把这些数移到相邻位置 2.消除逆序对。逆序对数量一定。移到相邻位置可以转化为将空格移动到两边,可以(线段树上)二分找到一个 \(mid\),\(mid\) 左边的往左移更优,右边的往右移更优,然后维护一下区间和就可以计算了(容斥思想)

D

推导:\(n\ge4\) 阶的强连通竞赛图存在 \((n-1)\) 阶的强连通竞赛子图 —> 对于 \(n\ge6\) 做多操作一次 —> 枚举被操作的点,判断是否强连通 [证明看这里](CF1268D Invertation in Tournament - 洛谷 | 计算机科学教育新生态 (luogu.com.cn))

如何快速断强连通?不难发现,如果非强连通,缩点后拓扑排序,最后一个 \(SCC\) 内的点出度最小。那么我们将点按出度排序,若存在 \(k<n\),前 \(k\) 个的出度之和为 \((_2^k)\),说明这 \(k\) 个构成最后一个 \(SCC\) 且和其他点无法构成 \(SCC\)。直接 \(sort\) 单次 \(nlogn\),用个桶排就没有 \(log\) 了,总复杂度 \(O(n^2)\)

下面的是 \(sort\) 的 \(n^2logn\) 的代码

#include <bits/stdc++.h>
using namespace std;
const int N = 2020;
int n, c[N], id[N], k[N]; char a[N][N];
pair<int, int> p[N], pp[N];
int judge () {
    int s = 0;
    for (int i = 1; i < n; ++i) {
        s += pp[i].first; if (s == i * (i - 1) / 2) return 0;
    }
    return 1;
}
signed main() {
    scanf ("%d", &n);
    for (int i = 1; i <= n; ++i) scanf ("%s", a[i] + 1);
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j) a[i][j] -= '0';
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j) if (a[i][j]) ++c[j];
    for (int i = 1; i <= n; ++i) p[i] = {c[i], i};
    sort (p + 1, p + n + 1);
    for (int i = 1; i <= n; ++i) id[p[i].second] = i;
    memcpy (pp, p, sizeof (p));
    if (judge ()) return puts ("0 1"), 0;
    if (n <= 6) { // brute
        int mx = 1 << n;
        int mn = 10000, cc = 0;
        for (int i = 0; i < mx; ++i) {
            int num = 0;
            for (int ii = 1; ii <= n; ++ii)
                num += (k[ii] = ((i >> (ii - 1)) & 1)), c[ii] = 0;
            if (num > mn) continue;
            for (int ii = 1; ii <= n; ++ii)
                for (int jj = 1; jj <= n; ++jj)
                    if (a[ii][jj] ^ k[ii] ^ k[jj]) ++c[jj];
            for (int ii = 1; ii <= n; ++ii) pp[ii] = {c[ii], ii};
            sort (pp + 1, pp + n + 1);
            if (judge ()) {
                if (num == mn) ++cc; else mn = num, cc = 1;
            }
        }
        if (mn == 10000) return puts ("-1"), 0;
        for (int i = 1; i <= mn; ++i) cc *= i;
        return printf ("%d %d\n", mn, cc), 0;
    }
    int res = 0;
    for (int i = 1; i <= n; ++i) { // 枚举翻转点
        memcpy (pp, p, sizeof (p));
        for (int j = 1; j <= n; ++j)
            if (a[i][j]) --pp[id[j]].first, ++pp[id[i]].first;
            else --pp[id[i]].first, ++pp[id[j]].first;
        sort (pp + 1, pp + n + 1); res += judge ();
    }
    return printf ("1 %d\n", res), 0;
}

E

对于“边权递增”的条件可以建一张新图:以边为节点,相邻的边之间小的向大的连边。要求的就是这张 \(DAG\) 上的可达点数量

根据定义,如果是树,直接一遍 \(dp\) 简单的求和就是答案。但仙人掌中的环会使答案产生重复。幸运的是,重复的地方只可能是环上的最小边,因为只有它可以往两个方向在环上走。更进一步,只有这个环上最小边往最大边走的两条路径都递增才会重复(两条路径可以在最大值处相交)。那么可以预处理出每个环是否有重复

具体做法:从大到小考虑每条边(\(DAG\) 的拓扑序),边权的 \(dp\) 是两个端点之和,如果这个环会产生重复且此边为最小就要减去最大边的 \(dp\)(即之后重合的部分)。点权的 \(dp\) 值亦在此过程中得出

标签:连通,环上,int,609,Codeforces,ii,num,Div,dp
来源: https://www.cnblogs.com/whx666/p/609-div1.html