2021牛客暑期多校训练营4 个人补题记录

比赛链接:Here

1001 - Course

1002 - Sample Game

1003 - LCS

构造,

首先排除不可能的情况

  • 两个 LCS 的值减去最小的 LCS 值如果比 (n) 还大,那么肯定构造不出来啊(不够长

剩下就是对于公共的 min(a,b,c)a ,对应的三种情况分别赋予 b,c,d 如果还是不够 (n) 则赋予不同值即可

Show Code

int main() {
    cin.tie(nullptr)->sync_with_stdio(false);
    int a, b, c, d, n;
    cin >> a >> b >> c >> n;
    d = min({a, b, c});
    string s1, s2, s3;
    if (a + c - d > n || a + b - d > n || b + c - d > n) {cout << "NO" << endl; return 0;}
    for (int i = 1; i <= d; i++) s1 += 'a', s2 += 'a', s3 += 'a';
    for (int i = 1; i <= a - d; i++) s1 += 'b', s2 += 'b';
    for (int i = 1; i <= b - d; i++) s2 += 'c', s3 += 'c';
    for (int i = 1; i <= c - d; i++) s1 += 'd', s3 += 'd';
    while (s1.size() < n) s1 += 'e';
    while (s2.size() < n) s2 += 'f';
    while (s3.size() < n) s3 += 'g';
    cout << s1 << endl << s2 << endl << s3 << endl;
}

1004 - Rebuild Tree

1005 - Tree Xor

1006 - Just a joke

讨论区有人提出

有两种操作:

  1. 删除一条边(边的数目-1)
  2. 删除个连通分量 (点的数目-k,边的数目-(k-1))

所以每次操作边和点的奇偶性都会变化,所以只需要判断边加点和的奇偶性即可。

int main() {
    cin.tie(nullptr)->sync_with_stdio(false);
    int n, m;
    cin >> n >> m;
    int x, y;
    for (int i = 0; i < m; i++) cin >> x >> y;
    if ((n + m) & 1) puts("Alice");
    else puts("Bob");
}

在赛时队友想到了用并查集求连通块量 + 单独的点的奇偶性

const int N = 10010;
int f[N], ans;
int find(int x) {return x == f[x] ? x : f[x] = find(f[x]);}
void merge(int x, int y) {
    x = find(x), y = find(y);
    if (x != y) f[y] = x;
    else ++ans;
}
int main() {
    cin.tie(nullptr)->sync_with_stdio(false);
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) f[i] = i;
    for (int i = 0, x, y; i < m; ++i) {
        cin >> x >> y;
        merge(x, y);
    }
    int cnt = 0;
    for (int i = 1; i <= n; ++i) {
        if (f[i] == i) cnt++;
    }
    cout << ((ans + cnt) % 2 == 0 ? "Bob
" : "Alice
");
}

1007 - Product

1008 - Convolution

1009 - Inverse Pair

对原数组处理以后求个逆序对即可

(a) 数组中选择合适的 (b_i in {0,1}) 来最小化逆序对数

求逆序对的方法有树状数组和归并排序,这里写归并排序简单一点

同时一定要开 long long 防止溢出

Show Code

using ll = long long;
 
const int N = 2e5 + 10;
ll a[N], tmp[N], n;
int vis[N];
ll ans = 0;
void Merge(int l, int m, int r) {
    int i = l;
    int j = m + 1;
    int k = l;
    while (i <= m && j <= r) {
        if (a[i] > a[j]) {
            tmp[k++] = a[j++];
            ans += m - i + 1;
        } else
            tmp[k++] = a[i++];
    }
    while (i <= m) tmp[k++] = a[i++];
    while (j <= r) tmp[k++] = a[j++];
    for (int i = l; i <= r; i++) a[i] = tmp[i];
}
void M_sort(int l, int r) {
    if (l < r) {
        int m = (l + r) >> 1;
        M_sort(l, m);
        M_sort(m + 1, r);
        Merge(l, m, r);
    }
}
int main() {
    cin.tie(nullptr)->sync_with_stdio(false);
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        if (vis[a[i] + 1] == 1) {
            a[i] += 1, vis[a[i] + 1] = 2;
        }  else vis[a[i]] = 1;
    }
    // for (int i = 1; i <= n; ++i) cout << a[i] << " 
"[i == n];
    M_sort(1, n);
    cout << ans << "
";
}

1010 - Average

为什么连板子都没看出来啊?明明比赛前一天还复习了单调队列优化DP的题

(a_{1...n})(b_{i...m}) 构成的数组形成的 (n imes m) 的矩阵求至少长度为 (x imes y) 的最大值子矩阵

我们要求至少长度为 (x imes y) 的最大值子矩阵,转化过来就是求出 (a_{1...n})(b_{i...m}) 中找一个最长的连续一段区间使其平均值最大。最后两者相加即可


二分答案平均值。

judge时把每一个a[i]-mid得到b[i]

在b[i]中找到一段合法的串使其权值和最大。

当最大权值和大于等于0时则mid上移。

求最大权值和用单调队列就行。(预处理b[i]的前缀和sum[i],队列中记录当前位置可选区间的最小的sum[i])

核心代码参考:Here

bool check(double x) {
    int i, l = 1, r = 0;
    for (i = 1; i <= n; i++)
        a[i] = (double)b[i] - x;
    sum[0] = 0;
    for (i = 1; i <= n; i++)
        sum[i] = sum[i - 1] + a[i];
    for (i = 1; i <= n; i++) {
        if (i >= s) {
            while (r >= l && sum[i - s] < sum[q[r]])r--;
            q[++r] = i - s;
        }
        if (l <= r && q[l] < i - t)l++;
        if (l <= r && sum[i] - sum[q[l]] >= 0)return true;
    }
    return false;
}
int main() {
    ...
    int ans = l = -10000; r = 10000;
    while (r - l > 1e-5) {
        mid = (l + r) / 2;
        if (check(mid)) ans = l = mid;
        else r = mid;
    }
    printf("%.3lf
", ans);
    return 0;
}

【AC Code】

Show Code

const int N = 1e5 + 10;
int pi[N];
double pp[N];
double solve(int k, int a) {
    double l = 0.0, r = 100001.0;
    for (int i = 1; i <= k; ++i) cin >> pi[i];
    while (r - l > 1e-8) {
        double m = (l + r) / 2.0, p = 0.0, sum = 0;
        for (int i = 1; i <= a; ++i) pp[i] = pp[i - 1] + pi[i] - m;
        sum = pp[a];
        for (int i = a + 1; i <= k; ++i) {
            p = min(p, pp[i - a]);
            pp[i] = pp[i - 1] + pi[i] - m;
            sum = max(sum, pp[i] - p);
        }
        if (sum >= 0) l = m;
        else r = m;
    }
    return r;
}
int main() {
    cin.tie(nullptr)->sync_with_stdio(false);
    int n, m, a, b;
    cin >> n >> m >> a >> b;
    cout << fixed << setprecision(10) << (solve(n, a) + solve(m, b));
}

The desire of his soul is the prophecy of his fate
你灵魂的欲望,是你命运的先知。

原文地址:https://www.cnblogs.com/RioTian/p/15085177.html