1015考试

1015考试总结

T1

​ 题目大意:

​ 现在有一张大小为𝑛*𝑚的网格图,小A用𝐾种颜色在网格图上作画。其中第𝑖种颜色编号为𝑖,初始时网格图中每个格子都没有颜色,编号为0。已知每种颜色小A都会使用且只使用一次,但使用的顺序是未知的。使用一种颜色时需要选定一个连续的子矩阵,将该子矩阵涂上这种颜色。后涂的颜色会覆盖之前的颜色。现在给出小A画完后的图,问有多少种颜色可能是小A最先使用的。

​ 对于100%的数据,1≤𝑛*𝑚, 𝑘≤10^5

​ 乱搞把。

​ 首先特判,当图中只有一种颜色的时候,需要输出\(k - 1\)而不是\(k\),因为这种颜色肯定不是最先使用的。

​ 考场上想出了一种很玄的做法,感觉会T,果然T了两个点,可是这种做法竟然是正解????题解就这么写的:统计每个数字的上下左右边界,然后对每个颜色扫一遍。感觉很玄啊竟然过了。

​ 真正的正解是机房另一个dalao想出来的(%%%):还是统计每个数字的上下左右边界,然后经每个数字对应的那块矩形内都加1(初始值为0)。用二维差分来做。最后看每个一点的数值是否大于一,如果大于1,那这一个点上对应的数字肯定是覆盖了其他数字的,就计入答案,复杂度\(O(n * m)\)

#include <bits/stdc++.h>

using namespace std;

inline long long read() {
    long long s = 0, f = 1; char ch;
    while(!isdigit(ch = getchar())) (ch == getchar()) && (f = -f);
    for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
    return s * f;
}

const int N = 5e5 + 5, inf = 1e9;
int n, m, k, ans, num, abc[N], sum[N];
struct col { int l, r, u, d, f, vis; } a[N];

int id(int x, int y) { return (x - 1) * m + y; }

void update(int x) {
    // if(a[x].u == inf || a[x].d == 0 || a[x].l == inf || a[x].r == 0) return ;
    sum[id(a[x].u, a[x].l)] ++; sum[id(a[x].u, a[x].r + 1)] --; sum[id(a[x].d + 1, a[x].l)] --; sum[id(a[x].d + 1, a[x].r + 1)] ++;
}

int main() {

    n = read(); m = read(); k = read();
    for(int i = 0;i <= k; i++) a[i].u = a[i].l = inf;
    for(int i = 1;i <= n; i++) 
        for(int j = 1, x;j <= m; j++) {
            cin >> x;
            if(a[x].vis == 0) a[x].vis = 1, num ++;
            abc[id(i, j)] = x;
            a[x].u = min(a[x].u, i); a[x].d = max(a[x].u, i);
            a[x].l = min(a[x].l, j); a[x].r = max(a[x].r, j);
        }
    if(num == 1) { printf("%d", k == 1 ? 1 : k - 1); return 0; }
    if(num == 0) { printf("%d", 0); return 0; }
    for(int i = 1;i <= k; i++) { if(!a[i].vis) continue; update(i); }
    for(int i = 1;i <= n; i++) 
        for(int j = 1;j <= m; j++) 
            sum[id(i, j)] = sum[id(i, j)] + sum[id(i - 1, j)] + sum[id(i, j - 1)] - sum[id(i - 1, j - 1)];
    for(int i = 1;i <= n; i++) 
        for(int j = 1;j <= m; j++) 
            if(sum[id(i, j)] > 1) a[abc[id(i, j)]].f = 1; 
    for(int i = 1;i <= k; i++) if(!a[i].f) ans ++;
    printf("%d", ans);

    fclose(stdin); fclose(stdout);
    return 0;
}

T2

​ 题目大意:

​ 有一个长度为𝑛的数列,数列中每个数都是[0, 𝑝−1]之间的整数。小A不知道数列中每个数的值,所以向小B做了𝑚次询问。每次小A会向小B询问一个区间[𝑙, 𝑟]中所有数的和对𝑝取模的结果。问完所有问题后,小A发现小B的回答中似乎存在矛盾。现在小A想找到最大的𝑋,满足小B的前𝑋次回答中不存在矛盾(𝑋有可能等于𝑚)。

​ 对于100%的数据,1≤𝑛, 𝑚≤106,2≤𝑝≤10^9

​ 带权并查集。

​ 我们有\(m\)个这样的式子:\((b[r] - b[l - 1]) \% p = k\)

​ 然后移项:\(b[r] \% p = (k + b[l - 1]) \% p\)

​ 然后用带权并查集维护差值就好了。
这里面有个图挺好的

#include <bits/stdc++.h>

#define ls(o) (o << 1)
#define rs(o) (o << 1 | 1)
#define mid ((l + r) >> 1)

using namespace std;

inline long long read() {
    long long s = 0, f = 1; char ch;
    while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
    for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
    return s * f;
}
 
const int N = 1e6 + 5, inf = 1e9;
int n, m, p, ans, siz[N], fa[N];
struct que { int l, r, x; } a[N];

int find(int x) {
    if(fa[x] != x) {
        int f = find(fa[x]);
        siz[x] += siz[fa[x]];
        fa[x] = f;
    }
    return fa[x];
}

int main() {

    n = read(); m = read(); p = read(); int tag = 0;
    for(int i = 1;i <= m; i++) a[i].l = read(), a[i].r = read(), a[i].x = read();
    for(int i = 0;i <= n; i++) fa[i] = i;
    for(int i = 1;i <= m; i++) {
        int tx = find(a[i].l - 1), ty = find(a[i].r);
        if(tx != ty) {
            fa[tx] = ty; siz[tx] = a[i].x + siz[a[i].r] - siz[a[i].l - 1];
        }
        else {
            if((siz[a[i].l - 1] - siz[a[i].r] + p) % p != a[i].x) { printf("%d", i - 1); tag = 1; break; }
        }
    }
    if(!tag) printf("%d", m);

    fclose(stdin); fclose(stdout);
    return 0;
}

T3

​ 题目大意:

​ 有𝑛个炸弹分布在一条数轴上。现在需要玩家以一定的能量引爆某一个炸弹,而炸弹爆炸会引起连锁反应导致更多的炸弹爆炸。炸弹爆炸时会携带能量𝑋,可以引爆和他距离≤𝑋的所有炸弹。当一个炸弹被能量𝑋的爆炸引爆时,该炸弹会携带⌊2*𝑋/3⌋(下取整)的能量。玩家可以设置第一个引爆的炸弹的能量,现在请问,如果想引爆所有炸弹,第一个引爆的炸弹的能量最少是多少呢?

​ 对于100%的数据,2≤𝑛≤10^6

​ 数据结构优化线性DP。

\(f[i]\)表示将\(i\)前面所有炸弹引爆所需要的最小能量,那么状态转移方程就是:\(f[i] = min(f[i], max(3 * f[j] / 2, x[i] - x[j]))\)

\(g[i]\)表示将\(i\)后面所有炸弹引爆所需要的最小能量,状态转移方程同理:\(g[i] = min(g[i], max(3 * g[j] / 2, x[j] - x[i]))\),记得倒叙枚举。

​ 然后就可以得到60pts的好成绩,再用单调队列优化即可满分。

#include <bits/stdc++.h>

using namespace std;

inline long long read() {
    long long s = 0, f = 1; char ch;
    while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
    for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
    return s * f;
}

const int N = 1e6 + 5, inf = 1e9;
int n, num, ans;
int x[N], f[N], g[N], q[N];

int main() {

    n = read();
    for(int i = 1;i <= n; i++) x[i] = read();
    sort(x + 1, x + n + 1);
    int l = 1, r = 1; q[l] = 1;
    for(int i = 2;i <= n; i++) {
        while(l < r && max((3 * f[q[l]] + 1) / 2, x[i] - x[q[l]]) > max((3 * f[q[l + 1]] + 1) / 2, x[i] - x[q[l + 1]])) l ++;
        f[i] = max((3 * f[q[l]] + 1) / 2, x[i] - x[q[l]]);
        while(l <= r && max((3 * f[i] + 1) / 2, x[i + 1] - x[i]) <= max((3 * f[q[r]] + 1) / 2, x[i + 1] - x[q[r]])) r --;//这里一定要写等于号
        q[++ r] = i;
    }
    l = 1, r = 1; q[l] = n; x[n + 1] = inf;
    for(int i = n - 1;i >= 1; i--) {
        while(l < r && max((3 * g[q[l]] + 1) / 2, x[q[l]] - x[i]) > max((3 * g[q[l + 1]] + 1) / 2, x[q[l + 1]] - x[i])) l ++;
        g[i] = max((3 * g[q[l]] + 1) / 2, x[q[l]] - x[i]);
        while(l <= r && max((3 * g[i] + 1) / 2, x[i] - x[i - 1]) <= max((3 * g[q[r]] + 1) / 2, x[q[r]] - x[i - 1])) r --; 
        q[++ r] = i;
    }
    ans = inf;
    for(int i = 1;i <= n; i++) ans = min(ans, max(f[i], g[i]));
    printf("%d", ans);

    return 0;
}
posted @ 2020-10-16 06:30  C锥  阅读(325)  评论(0编辑  收藏  举报