自嗨测试赛4


在出题组,没啥参考价值。出题的时候3个题一个都不会

A 简单的树叶

题目大意 : 每个点规定是儿子的值的min或max,k个叶子结点的值可以是1到k,每个数只能用一次,求根节点的最大值

  • 原题:CF1153D Serval and Rooted Tree

  • f[x]表示可决定x点值大小的最少有几个,因为点越少,值就可以越大

  • 当取max时f[x]是子节点的f值的min

  • 当去min时f[x]是子节点的f值的和

Code

Show Code
#include <cstdio>
#include <algorithm>

using namespace std;
const int N = 3e5 + 5;

int read(int x = 0, int f = 1, char c = getchar()) {
    for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
    for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
    return x * f;
}

struct Edge {
    int n, t;
}e[N];
int h[N], edc;

void Add(int x, int y) {
    e[++edc] = (Edge) {h[x], y}; h[x] = edc;
}

bool v[N];
int n, m;

int Dfs(int x) {
    if (!h[x]) return 1;
    int w = v[x] ? n : 0;
    for (int i = h[x]; i; i = e[i].n)
        w = v[x] ? min(w, Dfs(e[i].t)) : w + Dfs(e[i].t);
    return w;
}

int main() {
    freopen("leaf.in", "r", stdin);
    freopen("leaf.out", "w", stdout);
    n = read();
    for (int i = 1; i <= n; ++i)
        v[i] = read();
    for (int i = 2; i <= n; ++i)
        Add(read(), i);
    for (int i = 1; i <= n; ++i)
        if (!h[i]) m++;
    printf("%d
", m - Dfs(1) + 1);
    return 0;
}

B 老姚的服装

题目大意 : 给一个二维画布,在画布上规定区间找到面积最大的图案

  • 原题:CF1301E Nanosoft,注意本题颜色与原题不一样

  • 二维前缀和可以找到以每个点为左上角的图形,预处理每个长度的图形的出现情况的前缀和,回答的时候二分一个长度就好了

Code

Show Code
#include <cstdio>
#include <algorithm>

using namespace std;
const int N = 505;

int read(int x = 0, int f = 1, char c = getchar()) {
    for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
    for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
    return x * f;
}

char c[N];
int n, m, q;

struct Node {
    int s[N][N];

    void Init() {
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j)
                s[i][j] += s[i][j-1] + s[i-1][j] - s[i-1][j-1];
    }

    int Cal(int i, int j, int k, int l) {
        i--, j--;
        if (i + k > n || j + l > m) return -1;
        return s[i+k][j+l] - s[i][j+l] - s[i+k][j] + s[i][j];
    }
}s[4], f[N/2];

int main() {
    freopen("clothes.in", "r", stdin);
    freopen("clothes.out", "w", stdout);
    n = read(); m = read(); q = read();
    for (int i = 1; i <= n; ++i) {
        scanf("%s", c + 1);
        for (int j = 1; j <= m; ++j)
            s[c[j] == 'B' ? 0 : c[j] == 'W' ? 1 : c[j] == 'P' ? 2 : 3].s[i][j]++;
    }
    s[0].Init(); s[1].Init(); s[2].Init(); s[3].Init();
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            for (int k = 1, lim = min(n - i, m - j) + 1 >> 1; k <= lim && s[0].Cal(i, j, k, k) == k * k; ++k) 
                if (s[1].Cal(i, j + k, k, k) == k * k && s[2].Cal(i + k, j, k, k) == k * k && s[3].Cal(i + k, j + k, k, k) == k * k)
                    f[k].s[i][j]++, k = lim; 
    for (int k = min(n, m) >> 1; k >= 1; --k) f[k].Init();
    while (q--) {
        int lx = read(), ly = read(), rx = read(), ry = read();
        int l = 0, r = min(rx -= lx - 2, ry -= ly - 2) >> 1;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (f[mid].Cal(lx, ly, rx - mid * 2, ry - mid * 2)) l = mid;
            else r = mid - 1;
        }
        printf("%d
", 4 * l * l);
    }
    return 0;
}

C Shawk 的游戏

题目大意 : 问能否在两行里放一棵树,满足树边不相交

  • 原题是 CF573C Bear and Drawing,拉过来之后改成了多测

  • 网上的题解挺难懂,我和 HISKrrr 研究了一下午一点儿也没懂,最后发现挺简单,凯爹应该可以一眼切

解法一

  • 仔细观察样例的第二组数据,会发现只要n比13小那是不可能放不上去的,所以大力puts("Yes")就有10分了

解法二

  • 发现合法的方案一定可以找出一条主干放到第一行,然后剩下的可以在第二行放下,

  • 所以n2枚举主干是哪条链,然后主干下面一级的点最多有3个度,剩下的只能有2个度,Dfs一遍判断一下即可

  • 放个代码,时间复杂度 (O(n^3)),期望30分

Show Code
#include <cstdio>
#include <cstring>

using namespace std;
const int N = 1e5 + 5;

int read(int x = 0, int f = 1, char c = getchar()) {
    for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
    for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
    return x * f;
}

struct Edge {
    int n, t;
}e[N*2];
int h[N], edc, d[N];

void Add(int x, int y) {
    e[++edc] = (Edge) {h[x], y}; h[x] = edc; d[x]++;
}

bool v[N], g;
int n, a[N], cnt;

bool Dfs(int x, int fa, int k) {
    if (d[x] > k) return 0;
    int ans = 1;
    for (int i = h[x], y; i && ans; i = e[i].n)
        if ((y = e[i].t) != fa && !v[y]) ans = Dfs(y, x, 2);
    return ans;
}

void Solve() {
    int ans = 1;
    for (int x = 1; x <= cnt && ans; ++x)
        for (int i = h[a[x]], y; i && ans; i = e[i].n)
            if (!v[y=e[i].t]) ans = Dfs(y, 0, 3);
    if (ans) g = 1;
}

void Dfs(int x, int fa) {
    a[++cnt] = x; v[x] = 1;
    Solve();
    for (int i = h[x], y; i && !g; i = e[i].n)
        if ((y = e[i].t) != fa) Dfs(y, x);
    cnt--; v[x] = 0;
}

int main() {
    while (scanf("%d", &n) == 1) {
        memset(d + 1, 0, n * 4);
        memset(h + 1, 0, n * 4);
        g = 0; edc = 0;
        for (int i = 1; i < n; ++i) {
            int x = read(), y = read();
            Add(x, y); Add(y, x);
        }
        for (int i = 1; i <= n && !g; ++i) Dfs(i, 0);
        puts(g ? "Yes" : "No");
    }
    return 0;
}
  • 其实可以优化到叶子节点个数的平方乘n,但是叶子节点少了的话就大概率是Yes了(其实是不知道该怎么分配数据和分数了)

解法三

  • 可以枚举根结点,然后在子节点中枚举两个节点作为主干上的点,其他的节点放第二行,根据第几级儿子判断度数是否合适

  • 其他主干上的点只能向一侧延伸一个主干上的点,枚举这个点,其他节点放第二行,判断同上

  • 由于我太菜了,复杂度分析不出来,n=100的数据就让它过了,想卡的话可能是度数弄多一点

  • 参考代码

Show Code
#include <cstdio>
#include <cstring>

using namespace std;
const int N = 1e5 + 5;

int read(int x = 0, int f = 1, char c = getchar()) {
    for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
    for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
    return x * f;
}

struct Edge {
    int n, t;
}e[N*2];
int h[N], edc, d[N];

void Add(int x, int y) {
    e[++edc] = (Edge) {h[x], y}; h[x] = edc; d[x]++;
}

bool g;
int n;

bool Solve(int x, int fa, int k) {
    if (d[x] == 1) return 1;
    if (k && d[x] - 1 > k) return 0;
    if (!k) {
        for (int i = h[x]; i; i = e[i].n) {
            if (e[i].t == fa) continue;
            bool ans = Solve(e[i].t, x, k);
            for (int j = h[x]; j && ans; j = e[j].n)
                if (e[j].t != fa && i != j) ans = Solve(e[j].t, x, 2);
            if (ans) return 1;
        }
        return 0;
    }
    else {
        for (int i = h[x]; i; i = e[i].n)
            if (e[i].t != fa && !Solve(e[i].t, x, 1)) return 0;
        return 1;
    }
}

int main() {
    while (scanf("%d", &n) == 1) {
        memset(d + 1, 0, n * 4);
        memset(h + 1, 0, n * 4);
        edc = 0; g = 0;
        for (int i = 1; i < n; ++i) {
            int x = read(), y = read();
            Add(x, y); Add(y, x);
        }
        if (n == 1) g = 1;
        for (int x = 1; x <= n && !g; ++x) {
            if (d[x] == 1) g = Solve(e[h[x]].t, 0, 0);
            else {
                for (int i = h[x]; i && !g; i = e[i].n) {
                    for (int j = e[i].n; j && !g; j = e[j].n) {
                        g = Solve(e[j].t, x, 0) & Solve(e[i].t, x, 0);
                        if (g) {
                            for (int k = h[x]; k && g; k = e[k].n)
                                g = Solve(e[k].t, x, 2);
                        }
                    }
                }
            }
        }
        puts(g ? "Yes" : "No");
    }
    return 0;
}

解法四

  • 造完数据突然想起来解法三加个减枝会怎么样,就记一下Dfs了多少遍,超过1e7次就认为不可行,然后拿到了70分的好成绩,卡了一波,变成40分,倒这加边,还是40

  • 不过肯定还有别的减枝,当然减的越好得的分越多

解法五

  • 要提出一条链当作主干,放在第一行,然后其他的点放在第二行,然后考虑什么样的点不能放在第二行

    • 度为1的点显然可以

    • 度为二的点且另一边连出去的链会分叉

    • 度为三的点且另两边至少一条连出去的链会分叉

    • 度超过三的点,一行放不下

  • 如果将一个点看作是在主干上的点,周围连着1个或2个不能放在第二行的点,那么可以将这1个或两个点放在主干上,在多就放不下了,所以周围有3个及以上不能放在第二行的点的话,这颗树就不能在游戏里实现

  • 判断连出去的链会不会分叉,可以从叶子节点开始,将度数小于等于2的点删去,这样删去的点那一边连出去的链就一定不会分叉了(枚举点的时候就不枚举删去的点了)

  • 最后有个问题就是,如果有一个点周围有两个放不下的,然后就把这三个都放在主干上,这样就能放下。可是如果有很多个点都需要放在主干上,那会不会冲突呢,毕竟放在主干上的只能是一条链

  • 对于这个问题,就是如果冲突,就是说这些点不再一条链上,连出来的图形中有分叉点,可是想一下分叉点一定至少有三条路,这三个点一定都不能放在第二行,所以不会出现冲突

Code

Show Code
#include <cstdio>
#include <cstring>

using namespace std;
const int N = 1e5 + 5;

int read(int x = 0, int f = 1, char c = getchar()) {
    for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
    for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
    return x * f;
}

struct Edge {
    int n, t;
}e[N*2];
int h[N], edc, d[N];

void Add(int x, int y) {
    e[++edc] = (Edge) {h[x], y}; h[x] = edc; d[x]++;
}

bool v[N], g;
int n, c[N];

void Dfs(int x, int fa) {
    v[x] = 1;
    for (int i = h[x], y; i; i = e[i].n)
        if (d[y=e[i].t] == 2 && y != fa) Dfs(y, x);
}

int main() {
    freopen("sgame.in", "r", stdin);
    freopen("sgame.out", "w", stdout);
    while (scanf("%d", &n) == 1) {
        memset(v + 1, 0, n);
        memset(d + 1, 0, n * 4);
        memset(c + 1, 0, n * 4);
        memset(h + 1, 0, n * 4);
        g = 1; edc = 0;
        for (int i = 1; i < n; ++i) {
            int x = read(), y = read();
            Add(x, y); Add(y, x);
        }
        for (int i = 1; i <= n; ++i)
            if (d[i] == 1) Dfs(i, 0);
        for (int x = 1; x <= n; ++x)
            for (int i = h[x]; i; i = e[i].n)
                c[x] += v[e[i].t];
        for (int x = 1; x <= n && g; ++x) {
            if (v[x]) continue;
            int cnt = 0;
            for (int i = h[x]; i; i = e[i].n) {
                int y = e[i].t;
                if (d[y] == 2 && !c[y]) cnt++;
                else if (d[y] == 3 && !c[y]) cnt++;
                else if (d[y] == 3 && c[y] == 1) cnt++;
                else if (d[y] >= 4) cnt++;
            }
            if (cnt > 2) g = 0;
        }
        puts(g ? "Yes" : "No");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/shawk/p/14517633.html