8.1考试解题报告

\(\text{T1:}\)

题面:戳这里

\(Solution:\)

先跑两次 \(DP\),预处理出两个东西:

  • \(f1[i]\) 表示以第 \(i\) 个元素结尾的最大子段和(正序推)
  • \(f2[i]\) 表示以第 \(i\) 个元素开头的最大子段和(逆序推)

然后考虑枚举一个下标 \(i\),用于确定绝对选不到的那 \(k\) 个元素。
然后我们就需要知道在当前枚举的这个区域左边的 \(\max(f1[i])\) 以及右边的 \(\max(f2[i])\)
这个可以开两个 \(\text{ST}\) 表来实现我居然调了半个多小时
\(AC\)代码:

/*--------------------------------
  Code name: REIGN.cpp
  Author: The Ace Bee
  This code is made by The Ace Bee
--------------------------------*/
#include <cmath>
#include <cstdio>
#include <cstring>
#define rg register
#define int long long 
const int MAXN = 100010;
inline int max(int a, int b) { return a > b ? a : b; }
inline int read() {
    int s = 0; bool f = false; char c = getchar();
    while (c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while (c >= '0' && c <= '9') s = (s << 3) + (s << 1) + (c ^ 48), c = getchar();
    return f ? -s : s;
}
int n, k, a[MAXN];
int fp[MAXN], fn[MAXN];
int stp[MAXN][22], stn[MAXN][22];
inline int askp(int l, int r) {
    int x = log(r - l + 1) / log(2);
    return max(stp[l][x], stp[r - (1 << x) + 1][x]);
}
inline int askn(int l, int r) {
    int x = log(r - l + 1) / log(2);
    return max(stn[l][x], stn[r - (1 << x) + 1][x]);
}
signed main() {
    for (rg int T = read(); T; --T) {
        n = read(), k = read();
        for (rg int i = 1; i <= n; ++i) a[i] = read();
        memset(stp, 0xaf, sizeof stp);
        memset(stn, 0xaf, sizeof stn);
        for (rg int i = 1; i <= n; ++i) stp[i][0] = max(stp[i - 1][0] + a[i], a[i]);
        for (rg int i = n; i >= 1; --i) stn[i][0] = max(stn[i + 1][0] + a[i], a[i]);
        for (rg int j = 1; j <= 20; ++j)
            for (rg int i = 1; i <= n - (1 << j) + 1; ++i) {
                stp[i][j] = max(stp[i][j - 1], stp[i + (1 << (j - 1))][j - 1]);
                stn[i][j] = max(stn[i][j - 1], stn[i + (1 << (j - 1))][j - 1]);
            }
        int ans = -1e18;
        for (rg int i = 2; i + k <= n; ++i) {
            int xp = askp(1, i - 1);
            int xn = askn(i + k, n);
            ans = max(ans, xp + xn);
        }
        printf("%lld\n", ans);
    }
    return 0;
}

\(\text{T2:}\)

题面:戳这里

\(Solution:\)

首先我们需要倒着做,不然十分不好维护。
然后我们删边从后往前,每次都把那些可能影响答案的点丢去 \(\text{BFS}\)
删边的话可以用 \(\text{set}\)实现。
其实蛮好写的,但是考试时没多少时间了。。。\(QwQ\)
\(AC\) 代码:

/*--------------------------------
  Code name: TRIPS.cpp
  Author: The Ace Bee
  This code is made by The Ace Bee
--------------------------------*/
#include <set>
#include <queue>
#include <cstdio>
#define rg register
using namespace std;
const int MAXN = 200010;
inline int read() {
    int s = 0; bool f = false; char c = getchar();
    while (c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while (c >= '0' && c <= '9') s = (s << 3) + (s << 1) + (c ^ 48), c = getchar();
    return f ? -s : s;
}
int n, m, k, now;
int ans[MAXN], dgr[MAXN], del[MAXN];
set < int > G[MAXN];
pair < int, int > edge[MAXN];
inline void bfs(int s) {
    if (dgr[s] >= k || del[s]) return;
    queue < int > q;
    --now, del[s] = 1, q.push(s);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        for (auto v : G[u]) {
            --dgr[v];
            if (dgr[v] < k && !del[v])
                q.push(v), del[v] = 1, --now;
        }
    }
}
int main() {
    n = read(), m = read(), k = read();
    for (rg int u, v, i = 1; i <= m; ++i) {
        u = read(), v = read();
        ++dgr[u], ++dgr[v];
        edge[i] = make_pair(u, v);
        G[u].insert(v), G[v].insert(u);
    }
    now = n;
    for (rg int i = 1; i <= n; ++i) bfs(i);
    ans[m] = now;
    for (rg int i = m; i >= 1; --i) {
        int u = edge[i].first, v = edge[i].second;
        if (!del[u]) --dgr[v];
        if (!del[v]) --dgr[u];
        G[u].erase(v), G[v].erase(u);
        bfs(u), bfs(v), ans[i - 1] = now;
    }
    for (rg int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
    return 0;
}

\(\text{T3}\)

题面:戳这里

\(Solution\)

考虑建图,将两个相邻的格子之间建有向边,完了直接 \(\text{Floyd}\) 判断连通性即可。
\(AC\) 代码:

/*--------------------------------
  Code name: SCC.cpp
  Author: The Ace Bee
  This code is made by The Ace Bee
--------------------------------*/
#include <cstdio>
#include <cstring>
#define rg register
const int MAXN = 410;
inline int read() {
    int s = 0; bool f = false; char c = getchar();
    while (c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while (c >= '0' && c <= '9') s = (s << 3) + (s << 1) + (c ^ 48), c = getchar();
    return f ? -s : s;
}
char rr[25], cc[25];
int r, c, pos[30][30], f[MAXN][MAXN];
int main() {
    r = read(), c = read();
    for (rg int i = 1; i <= r; ++i)
        for (rg int j = 1; j <= c; ++j)
            pos[i][j] = (i - 1) * c + j;
    char rr[30]; scanf("%s", rr + 1);
    char cc[30]; scanf("%s", cc + 1);
    for (rg int i = 1; i <= r; ++i) {
        if (rr[i] == '>')
            for (rg int j = 1; j < c; ++j) f[pos[i][j]][pos[i][j + 1]] = 1;
        else
            for (rg int j = 1; j < c; ++j) f[pos[i][j + 1]][pos[i][j]] = 1;
    }
    for (rg int j = 1; j <= c; ++j) {
        if (cc[j] == 'v')
            for (rg int i = 1; i < r; ++i) f[pos[i][j]][pos[i + 1][j]] = 1;
        else
            for (rg int i = 1; i < r; ++i) f[pos[i + 1][j]][pos[i][j]] = 1;
    }
    for (rg int k = 1; k <= pos[r][c]; ++k)
        for (rg int i = 1; i <= pos[r][c]; ++i)
            for (rg int j = 1; j <= pos[r][c]; ++j)
                if (i != j && j != k && i != k)
                    f[i][j] = f[i][j] | (f[i][k] & f[k][j]);
    for (rg int i = 1; i <= pos[r][c]; ++i)
        for (rg int j = 1; j <= pos[r][c]; ++j)
            if (i != j && !f[i][j]) return puts("NO"), 0;
    puts("YES");
    return 0;
}

\(\text{T4:}\)

题面:戳这里

\(Solution:\)

其实这是一道大大的数学题
考虑几何概型。
首先我们要明确有解的条件: 将长度为 \(k\) 的一条线段分割成 \(n\) 段,使得这 \(n\) 段可以组成一个闭合的多边形。
这里首先要来一个小结论:周长为 \(k\)\(n\) 边形,最大的一条边长小于 \(k/2\)

小小的证明:
如果存在一条边它的长度大于等于 \(k/2\),那么其他所有边的长度之和都比这一条边小,显然不合理。

然后,我们如果要把一条线段给分成 \(n\) 段,也就是要在线段上取 \(n-1\) 个点。
然后我们发现正着搞不好搞,考虑补集思想。
根据我们之前的小结论,不合法的情况当且仅当存在一条线段的模大于等于 \(k/2\)
也就是说,我们在线段上取的这 \(n-1\) 个点都必须落在线段的同一半边。
这个概率可以直接算: \(p=C_n^1/2^{n-1}\)
(分子是确定一条不合法的长过 \(k/2\) 的边,分母是 \(n-1\) 个点都以 \(1/2\) 的概率落在线段的同一半一边的概率)
然后用 \(1\) 去减就好了: \(Ans=1-n/2^{n-1}\)
\(AC\) 代码:

/*--------------------------------
  Code name: EQUILIBER.cpp
  Author: The Ace Bee
  This code is made by The Ace Bee
--------------------------------*/
#include <cstdio>
#define rg register
#define int long long 
inline int read() {
    int s = 0; bool f = false; char c = getchar();
    while (c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while (c >= '0' && c <= '9') s = (s << 3) + (s << 1) + (c ^ 48), c = getchar();
    return f ? -s : s;
}
const int p = 1e9 + 7;
inline int power(int x, int k) {
    int res = 1;
    for (; k; k >>= 1, x = 1ll * x * x % p)
        if (k & 1) res = 1ll * res * x % p;
    return res % p;
}
signed main() {
    int n = read(); read();
    printf("%lld\n", 1ll * (power(2, n - 1) - n + p) * power(2, (n - 1) * (p - 2)) % p);
    return 0;
}

\(\text{T5}\)

题面:戳这里

\(Solution:\)

首先我们发现原数列中,各个数的位置无关紧要。
所以我们只要开个桶就好了。
但是!!!
输入数据有 \(10^9\)...
这里用到一个贪心思想解决:
如果一个数大于了 \(n\times2\),就直接把它丢掉
为什么呢?
首先我们对于该序列只有两种手段,插入一个数或者删除一个数。
而对于大于 \(n\times2\) 的数,删掉它的代价一定优于插入至少 \(n\) 个元素的代价。
所以读入时,先把这些数丢掉。
接下来考虑一下 \(DP\) (好像贪心也能做)
我们记录两个值 \(mtong,maxx\) 分别记录最大的 \(tong\) 的值以及数列中的最大数值。
然后再枚举每一个数值 \(i \in [1,maxx]\)\(f[j]\) 表示枚举到当前时刻
构成以长度为 \(j\) 的排列所需要的最小步数。
考虑初始化和转移:(直接写在代码里面了)
\(AC\) 代码:

/*--------------------------------
  Code name: PERMART.cpp
  Author: The Ace Bee
  This code is made by The Ace Bee
--------------------------------*/
#include <cstdio>
#include <algorithm>
#define rg register
#define int long long 
const int MAXN = 1000010;
inline int read() {
    int s = 0; bool f = false; char c = getchar();
    while (c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while (c >= '0' && c <= '9') s = (s << 3) + (s << 1) + (c ^ 48), c = getchar();
    return f ? -s : s;
}
int n, f[MAXN << 1], tong[MAXN << 1];
signed main() {
    for (rg int del, maxx, T = read(); T; --T) {
        n = read(), del = 0, maxx = 0;
        for (rg int x, i = 1; i <= n; ++i) {
            x = read();
            if (x > 2 * n) ++del;
            else ++tong[x], maxx = std ::max(maxx, x);
        }
        int mtong = 0;
        for (rg int i = 1; i <= maxx; ++i)
            mtong = std ::max(mtong, tong[i]);
        for (rg int i = 0; i <= mtong; ++i)
            f[i] = std ::abs(tong[1] - i);
        for (rg int i = 2; i <= maxx; ++i) {
            for (rg int j = mtong - 1; ~j; --j) f[j] = std ::min(f[j], f[j + 1]);
            for (rg int j = mtong; ~j; --j) f[j] += std ::abs(tong[i] - j);
        }
        int ans = 1e9;
        for (rg int i = 0; i <= mtong; ++i) ans = std ::min(ans, f[i]);
        printf("%lld\n", ans + del);
        for (rg int i = 0; i <= maxx; ++i) tong[i] = 0;
    }
    return 0;
}

\(\text{T6:}\)

题面:戳这里

\(Solution:\)

还是考虑建图。
先将相邻的两个点连边。
对于数值的元素,开一个邻接表,为了降低边的数目可以建菊花图傻逼才去建完全图,将边的权处理一下:

  • 对于相邻两点之间的边,权为2
  • 对于菊花图中的边,权为1

跑完最短路后,把答案除以 \(2\) 即可
\(AC\) 代码:

/*--------------------------------
  Code name: DIGJUMP.cpp
  Author: The Ace Bee
  This code is made by The Ace Bee
--------------------------------*/
#include <queue>
#include <cstdio>
#include <cstring>
#define rg register
#define int long long 
const int MAXN = 100100;
const int MAXM = 300010;
using namespace std;
inline int read() {
    int s = 0; bool f = false; char c = getchar();
    while (c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while (c >= '0' && c <= '9') s = (s << 3) + (s << 1) + (c ^ 48), c = getchar();
    return f ? -s : s;
}
int tot, head[MAXN], nxt[MAXM << 1], ver[MAXM << 1], w[MAXM << 1];
inline void Add_edge(int u, int v, int d)
{ nxt[++tot] = head[u], head[u] = tot, ver[tot] = v, w[tot] = d; }
int tong[15], tt, hd[15], nt[MAXN], vr[MAXN];
inline void Add_relate(int u, int v)
{ nt[++tt] = hd[u], hd[u] = tt, vr[tt] = v; }
int n, exi[MAXN], dis[MAXN];
queue < int > q;
inline void spfa(int s) {
    memset(exi, 0, sizeof exi);
    memset(dis, 0x7f, sizeof dis);
    q.push(s), exi[s] = 1, dis[s] = 0;
    while (!q.empty()) {
        int u = q.front(); q.pop(), exi[u] = 0;
        for (rg int i = head[u]; i; i = nxt[i]) {
            int v = ver[i];
            if (dis[v] > dis[u] + w[i]) {
                dis[v] = dis[u] + w[i];
                if (!exi[v]) exi[v] = 1, q.push(v);
            }
        }
    }
}
signed main() {
    char s[MAXN];
    scanf("%s", s + 1), n = strlen(s + 1);
    for (rg int x, i = 1; i <= n; ++i) {
        x = s[i] ^ 48, ++tong[x], Add_relate(x, i);
        if (i != n) Add_edge(i, i + 1, 2), Add_edge(i + 1, i, 2);
    }
    for (rg int x = 0; x < 10; ++x)
        if (tong[x] > 1)
            for (rg int i = hd[x]; i; i = nt[i])
                Add_edge(n + x + 1, vr[i], 1), Add_edge(vr[i], n + x + 1, 1);
    spfa(1);
    printf("%lld\n", dis[n] >> 1);
    return 0;
}

完结撒花\(qwq\)

猜你喜欢

转载自www.cnblogs.com/zsbzsb/p/11285059.html