zoukankan      html  css  js  c++  java
  • 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

    推导:(nge4) 阶的强连通竞赛图存在 ((n-1)) 阶的强连通竞赛子图 —> 对于 (nge6) 做多操作一次 —> 枚举被操作的点,判断是否强连通 证明看这里

    如何快速断强连通?不难发现,如果非强连通,缩点后拓扑排序,最后一个 (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
    ", 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
    ", res), 0;
    }
    

    E

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

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

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

  • 相关阅读:
    【C/C++】最长公共子序列(LCS)/动态规划
    【C/C++】vector 动态二维数组
    【C/C++】string的长度
    【C/C++】最长不下降子序列/动态规划
    【C/C++】最大连续子序列和/动态规划
    【C/C++】输入:连续输入,以逗号隔开
    【C/C++】链表/ListNode/数据结构
    【Matlab】abs不支持复整数
    获取正在运行的服务
    从源码的角度分析Volley加载数据的过程
  • 原文地址:https://www.cnblogs.com/whx666/p/609-div1.html
Copyright © 2011-2022 走看看