【解题总结】Latin American Regional Contest 2019(Codeforces Gym 102428)

我解决的:E、I、F。

没看的:C、H。

旁观的:G、M、B、D、L、K。

看了但没做出来的:A、J。

E Eggfruit Cake

简单题,略。

I Improve SPAM

简单拓扑排序,略。

M Mountain Ranges

简单题,略。

K Know your Aliens

简单构造题,略。

F Fabricating Sculptures

题意:给定 S , B ( B S ) S, B(B \ge S) ,求出所有“单峰拆分”的方案数。单峰拆分即将 B B 拆分成 S S 个正整数 { a i } \left\lbrace a_i\right\rbrace ,且 t [ 1 , S ] \exists t \in [1, S] ,使得 a 1 a_1 a t a_t 不降, a t a_t a S a_S 不增。 B , S 5000 B, S \le 5000

f ( i , j ) f(i, j) 为将 j j 拆分成 i i 个数的“单峰拆分”的方案数。

对于 i < j i < j ,显然 f ( i , j ) = 0 f(i, j) = 0

对于 i = j i = j ,那么 f ( i , j ) = 1 f(i, j)=1

对于 j > i j>i ,考虑将每一个数都减去 1,然后看看剩余的非 0 数有多少。于是
f ( i , j ) = 1 k i ( i k + 1 ) f ( k , j i ) f(i, j) = \sum_{1 \le k \le i} (i-k+1) f(k, j - i)
其中 k k 就表示剩余的非 0 数个数,这些数可以来自 [ 1 , i ] [1, i] 的长为 k k 的子区间的任意一个,所以要乘一个 i k + 1 i-k+1

用两个前缀和优化转移即可做到时间复杂度为 O ( B S ) O(BS)

G Gluing Pictures

题意:给定串 S S ,多次询问串 T T ,问 T T 最少要用几个 S S 的子串构成。

辅以 SAM 贪心即可。

A Algorithm Teaching

DAG 上最长反链,略。我不知道这个模型,没做出来…

L Leverage MDT

题意:给一个 01 矩阵,可以选择一些行翻转。问可能达到的最大全 1 子矩阵大小。

由于可以翻转,因此只要一段值都相等就有可能构成子矩阵。也就是说,00001111 作为答案的一部分时,是没有区别的。

因此可以枚举作为最右端的列,算一下最靠右段的长度,然后就转化为了一个经典的单调栈问题。

#include <bits/stdc++.h>
using namespace std;
int n, m;
char s[1005][1005];
int h[1005], st[1005][2], top;
int main(){
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; ++i){
        scanf("%s", s[i]);
        h[i] = 1;
    }
    h[n] = 0;
    int ans = 1;
    for (int i = 1; i < m; ++i){
        for (int j = 0; j < n; ++j){
            if (s[j][i] == s[j][i - 1])
                ++h[j];
            else h[j] = 1;
        }
        top = 0;
        for (int j = 0; j <= n; ++j){
            int curw = 0;
            while (top > 0 && h[j] <= st[top][0]){
                curw += st[top][1];
                ans = max(ans, min(curw, st[top][0]));
                --top;
            }
            st[++top][0] = h[j], st[top][1] = curw + 1;
        }
    }
    printf("%d\n", ans * ans);
    return 0;
}

C Cut Inequality Down

题意:有 N N 天,第 i i 天可以获得 a i a_i 元。第 i i 天结束时,手头上的钱要和 L L max \max ,和 U U min \min 。多组询问,问若第 B B 天开始时有 X X 元,那么第 E E 天结束时有多少钱。

称钱超过 U U 为上溢,少于 L L 为下溢。容易发现,第 i i 天结束时如果溢出了,那么后面的钱的变化是确定的。当然,如果始终不溢出,那么最终的钱也是确定的。

于是我们可以用 ST 表和二分算出:某天开始时,如果有 X X 元,那么之后最早哪一天会溢出。然后倍增处理一下:第 i i 天开始时如果钱是 U U 或者 L L ,那么之后最早哪一天会溢出,是上溢还是下溢。

预处理完后跑倍增回答询问即可。时间复杂度 O ( ( N + Q ) log N ) O((N+Q) \log N)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read(){
    int f = 1, x = 0;
    char c = getchar();
    while (c < '0' || c > '9'){if(c == '-') f = -f; c = getchar();}
    while (c >= '0' && c <= '9')x = x * 10 + c - '0', c = getchar();
    return f * x; 
}
int n, L, U, a[100005], logg[100005];
ll sum[100005], f[100005][18], g[100005][18];
int nxt[100005][18][2][2] = {0};
inline ll getmax(int l, int r){
    int p = logg[r - l + 1];
    return max(f[l][p], f[r - (1 << p) + 1][p]);
}
inline ll getmin(int l, int r){
    int p = logg[r - l + 1];
    return min(g[l][p], g[r - (1 << p) + 1][p]);
}
int gethi(int st, int x){
    int l = st, r = n + 1;
    while (r > l){
        int mid = (l + r) >> 1;
        if (x - sum[st - 1] + getmax(st, mid) >= U)
            r = mid;
        else l = mid + 1;
    }
    return l > n ? 0: l;
}
int getlo(int st, int x){
    int l = st, r = n + 1;
    while (r > l){
        int mid = (l + r) >> 1;
        if (x - sum[st - 1] + getmin(st, mid) <= L)
            r = mid;
        else l = mid + 1;
    }
    return l > n ? 0: l;
}
void init(){
    n = read(), L = read(), U = read();
    sum[0] = 0;
    for (int i = 1; i <= n; ++i){
        a[i] = read(), sum[i] = sum[i - 1] + a[i], 
        f[i][0] = g[i][0] = sum[i];
    }
    logg[0] = -1;
    for (int i = 1; i <= n; ++i)
        logg[i] = logg[i >> 1] + 1;
    for (int j = 1; j <= logg[n]; ++j){
        int p = (1 << j);
        for (int i = 1; i + p - 1 <= n; ++i){
            f[i][j] = max(f[i][j - 1], f[i + (p >> 1)][j - 1]);
            g[i][j] = min(g[i][j - 1], g[i + (p >> 1)][j - 1]);
        }
    }
    for (int i = n, rb1, rb2; i >= 1; --i){
        rb1 = gethi(i, U), rb2 = getlo(i, U);
        if (!rb2 || (rb1 > 0 && rb1 < rb2))
            nxt[i][0][0][0] = rb1, nxt[i][0][0][1] = 0;
        else
            nxt[i][0][0][0] = rb2, nxt[i][0][0][1] = 1;

        rb1 = gethi(i, L), rb2 = getlo(i, L);
        if (!rb2 || (rb1 > 0 && rb1 < rb2))
            nxt[i][0][1][0] = rb1, nxt[i][0][1][1] = 0;
        else
            nxt[i][0][1][0] = rb2, nxt[i][0][1][1] = 1;

        for (int j = 1, to, tp; ; ++j){
            bool flag = false;
            to = nxt[i][j - 1][0][0] + 1, tp = nxt[i][j - 1][0][1];
            if (to > 1){
                nxt[i][j][0][0] = nxt[to][j - 1][tp][0];
                nxt[i][j][0][1] = nxt[to][j - 1][tp][1];
                flag = true;
            }
            to = nxt[i][j - 1][1][0] + 1, tp = nxt[i][j - 1][1][1];
            if (to > 1){
                nxt[i][j][1][0] = nxt[to][j - 1][tp][0];
                nxt[i][j][1][1] = nxt[to][j - 1][tp][1];
                flag = true;
            }
            if (!flag) {
                break;
            }
        }
    }
}
void query(int st, int ed, int x){
    int rb1 = gethi(st, x), rb2 = getlo(st, x);
    if (rb1 == 0) rb1 = n + 1;
    if (rb2 == 0) rb2 = n + 1;
    int o;
    if (rb1 < rb2){
        if (rb1 > ed) {
            printf("%lld\n", x + sum[ed] - sum[st - 1]);
            return ;
        } else if (rb1 == ed) {
            printf("%d\n", U);
            return ;
        } else {
            st = rb1 + 1, x = U, o = 0;
        }
    } else {
        if (rb2 > ed) {
            printf("%lld\n", x + sum[ed] - sum[st - 1]);
            return ;
        } else if (rb2 == ed) {
            printf("%d\n", L);
            return ;
        } else {
            st = rb2 + 1, x = L, o = 1;
        }
    }
    for (int j = logg[n]; j >= 0; --j){
        int to = nxt[st][j][o][0], tp = nxt[st][j][o][1];
        if (!to) continue;
        if (to == ed) {
            printf("%d\n", (tp == 0 ? U: L));
            return ;
        } else if (to < ed){
            st = to + 1, x = (tp == 0 ? U: L), o = tp;
        }
    }
    printf("%lld\n", x + sum[ed] - sum[st - 1]);
}
void solve(){
    int q = read();
    while (q--){
        int st = read(), ed = read(), x = read();
        query(st, ed, x);
    }
}
int main(){
    init();
    solve();
    return 0;
}

D Dazzling stars

待补。。。

J Jumping Grasshoper

待补。。。

H Hold or Continue?

待补。。。

猜你喜欢

转载自blog.csdn.net/zqy1018/article/details/108241698