zoukankan      html  css  js  c++  java
  • 【解题报告】杭电多校 round 1

    还有好多题要补qwq

    1006 - Xor sum

    题意简介

    给你一个长度为 (n) 的数组,要求你选出其中最小的区间,使得这个区间的异或和比 (k) 大。

    思路分析

    看到异或,想到线性基和 Trie,这道题用的是后者。

    从左往右扫,对每个 r,先维护当前的前缀异或和,然后在 Trie 中模拟异或的过程,从高位往低位,对于让答案大于 k 的选项,直接尝试更新答案。

    从高位往低位转移的过程中,往异或路径可以使前几位(也就是已经扫到的高位)与 k 值一致的方向移动即可。

    具体实现看代码。

    解题代码

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    const int N = 1e5+5, M = 30;
    int to[N*M][2], pos[N*M], k, cn;
    inline void debug(int x) {
        for(int i = 29; i >= 0; i--) {
            if((x & (1 << i))) putchar('1');
            else putchar('0');
        }
    }
    inline void insert(int x, int id) {
        int u = 1, d = 0;
        for(int i = 29; i >= 0; i--) {
            d = ((1 << i) & x) ? 1 : 0;
            if(to[u][d] == 0) to[u][d] = ++cn;
            u = to[u][d];
            pos[u] = id;
        }
    }
    inline int get_L(int x) {
        int u = 1, bit_k, bit_x, L = -1;
        if(x >= k) L = 0;
        // printf("Now finding %d to %d
    ", x, k);
        for(int i = 29; i >= 0 && u; i--) {
            bit_k = ((1 << i) & k) ? 1 : 0;
            bit_x = ((1 << i) & x) ? 1 : 0;
            if(bit_x > bit_k) {
                // 这一位已经更大了 那如果能找到一个0来异或 答案总是能比k大的。
                if(to[u][0] != 0) L = max(L, pos[to[u][0]]);
                // 针对异或后一样的情况 继续往下找
                u = to[u][1];
                // printf("1");
                continue;
            }
            if(bit_k == 0) {
                // 只要能找一个 1 来异或那必然是更大的
                if(to[u][1] != 0) L = max(L, pos[to[u][1]]);
                u = to[u][0];
                // printf("0");
                continue;
            }
            // 两都是1 那的要个0 只有自己是0 那得要个1
            if(bit_x == bit_k) u = to[u][0];
            else u = to[u][1];
            // printf(bit_x == bit_k ? "*0*" : "*1*");
        }
        // 成功走到了末尾 也就是异或
        if(u) L = max(L, pos[u]);
        // puts("");
        // printf("Get L = %d
    ", L);
        return L;
    }
    inline void clear_Trie() {
        for(int i = 1; i <= cn; i++) to[i][0] = to[i][1] = pos[i] = 0;
    }
    int main() {
        freopen("in.txt", "r", stdin);
        freopen("out.txt", "w", stdout);
        int t, n, sum, l, ansl, len, x;
        scanf("%d", &t);
        while(t--) {
            scanf("%d%d", &n, &k);
            // printf("K = %d ", k); debug(k); puts("");
            clear_Trie();
            sum = 0, len = N, cn = 1;
            for(int i = 1; i <= n; i++) {
                scanf("%d", &x);
                sum ^= x;
                // printf("Now: %d ", x); debug(x); puts("");
                // printf("Sum: %d ", sum); debug(sum); puts("");
                l = get_L(sum);
                if(l != -1 && i - l < len) {
                    len = i - l;
                    ansl = l + 1;
                }
                insert(sum, i);
            }
            if(len != N) printf("%d %d
    ", ansl, ansl + len - 1);
            else printf("-1
    ");
        }
        return 0;
    }
    

    1008 - Maximal submatrix

    题意简介

    给你一个 (n imes m) 的矩阵,要求你找出其中的最大子矩阵,使得这个最大子矩阵每列的元素都是随行数递增而不严格递增的。

    其中 (n,m leq 2000) , 要求输出这个子矩阵的位置。

    思路分析

    首先,把每列单独拆出来,分别 dp,求解其对应的最长非递减子段的长度。

    我们记 (f(i,j)) 表示在第 j 列中,以第 i 个元素为末尾元素的,最长的非递减字段的长度。

    显然地,我们有转移:

    [f(i, j) = egin{cases} 1, &A(i, j) < A(i-1, j) \ f(i - 1, j) + 1, &A(i,j) geq A(i-1,j) end{cases}]

    现在,我们考虑如何求出答案。

    一开始,我们尝试对每个位置 ((i,j)) ,求出以 (x = i, y = j) 为为底边的最大矩形。但是想要做到这样,需要 (O(n^3)),我们需要一种线性或是带 log 的复杂度。

    于是,我们想到,可以对于每个点 ((i,j)) 求出其恰以 (f(i,j)) 为高的矩阵最大面积。因为高比 (f(i,j)) 小的矩阵我们一定会在这一行的别的点处被枚举到。

    具体的操作,我们使用了单调栈。通过单调栈,可以找到同一行的,可向上扩展距离(也就是 (f(x, y)))大于等于 (f(i, j)) 的最远位置,然后求出面积 (f(i, j) imes (R(i, j) - L(i, j) + 1)),尝试更新答案。

    解题代码

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    const int N = 2005, M = 2005;
    int T, n, m, A[N][M], f[N][M], Left[M], Right[M], st[M], res;
    int main() {
        scanf("%d", &T);
        while(T--) {
            res = 0;
            memset(f, 0, sizeof(f));
            scanf("%d%d", &n, &m);
            for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) scanf("%d", A[i]+j);
            for(int j=1; j<=m; j++) for(int i=1; i<=n; i++) {
                f[i][j] = A[i][j] >= A[i-1][j] ? f[i-1][j] + 1 : 1;
            }
            for(int i=1; i<=n; i++) {
                // st[i] 为第 i 行的单调栈
                int &top = st[0];
                top = 0;
                memset(Left, 0, sizeof(Left));
                memset(Right, 0, sizeof(Right));
                for(int j=1; j<=m; j++) {
                    while(top && f[i][st[top]] >= f[i][j]) {
                        Left[j] = Left[st[top]];
                        top--;
                    }
                    if(!Left[j]) Left[j] = j;
                    st[++top] = j; 
                }
                top = 0;
                for(int j=m; j>=1; j--) {
                    while(top && f[i][st[top]] >= f[i][j]) {
                        Right[j] = Right[st[top]];
                        top--;
                    }
                    if(!Right[j]) Right[j] = j;
                    st[++top] = j; 
                }
                for(int j=1; j<=m; j++) {
                    res = max(res, (Right[j] - Left[j] + 1) * f[i][j]);
                }
            }
            printf("%d
    ", res);
        }
        return 0;
    }
    

    1010 - zoto

    题意简介

    告诉你 (n) 个点 ((i,y_i)),然后有 (m) 个询问,给出一个矩形的左下角和右上角的坐标,问你这个区域内能找到多少种不同的 (y) 值。

    其中,(n,m,y_i < 100000)

    思路简介

    一眼想到莫队。用树状数组维护区间答案。复杂度是 (O(nsqrt{n}log{n}))。但是,这样会 T 的,这道题的做法更巧妙。本题是除了莫队外,还对值域根号分块,强制将修改复杂度定为 (O(1)) ,即只修改单点和对应块的;然后查询时,整块直接取,余点暴力算。

    这样凑出来,莫队的复杂度刚好是:(O(nsqrt{n} + msqrt{N})) 。((N) 是值域)。

    解题代码

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    using namespace std;
    const int N = 1e5 + 5, M = 1e3 + 5;
    int T, n, m, block[M], cnt[N], y[N], bn, by, Ans[N];
    struct Query {
        int xl, xr, yl, yr, i, bl;
        Query() {
            xl = xr = yl = yr = i = bl = 0;
        }
        bool operator < (const Query &B) const {
            if(bl != B.bl) return bl < B.bl;
            return (bl & 1) ? (xr < B.xr) : (xr > B.xr);
        }
    } Q[N];
    inline void add_value(int y) {
        cnt[y]++;
        if(cnt[y] == 1) block[y / by]++;
    }
    inline void restore(int y) {
        cnt[y]--;
        if(cnt[y] == 0) block[y / by]--;
    }
    inline int get_Ans(int l, int r) {
        int bl = l / by, br = r / by, res = 0;
        if(bl == br) {
            for(int i = l; i <= r; i++) res += cnt[i] ? 1 : 0;
            return res;
        }
        for(int i = bl + 1; i < br; i++) res += block[i];
        for(int i = bl * by + by - 1; i >= l; i--) res += cnt[i] ? 1 : 0;
        for(int i = br * by; i <= r; i++) res += cnt[i] ? 1 : 0;
        return res;
    }
    int main() {
        // freopen("in.txt", "r", stdin);
        // freopen("out.txt", "w", stdout);
        scanf("%d", &T);
        while(T--) {
            scanf("%d%d", &n, &m);
            bn = (int) sqrt(n) + 1, by = 0;
            for(int i = 1; i <= n; i++) {
                scanf("%d", y + i);
                by = max(by , y[i]);
            }
            memset(block, 0, sizeof(block));
            memset(cnt, 0, sizeof(cnt));
            for(int i = 1; i <= m; i++) {
                scanf("%d%d%d%d", &Q[i].xl, &Q[i].yl, &Q[i].xr, &Q[i].yr);
                Q[i].bl = (Q[i].xl - 1) / bn + 1;
                Q[i].i = i;
                by = max(by, Q[i].yr);
            }
            by = (int) sqrt(by) + 1;
            sort(Q + 1, Q + m + 1);
            int l = 1, r = 0;
            for(int i = 1; i <= m; i++) {
                while(r < Q[i].xr) add_value(y[++r]);
                while(l > Q[i].xl) add_value(y[--l]);
                while(r > Q[i].xr) restore(y[r--]);
                while(l < Q[i].xl) restore(y[l++]);
                Ans[Q[i].i] = get_Ans(Q[i].yl, Q[i].yr);
            }
            for(int i = 1; i <= m; i++) {
                printf("%d
    ", Ans[i]);
            }
        }
        return 0;
    }
    

    (来自 睁眼,便是上午十一点

  • 相关阅读:
    postman(十一):添加cookie
    基于百度通用翻译API的一个翻译小工具
    python之做一个简易的翻译器(二)
    python之做一个简易的翻译器(一)
    如何查看linux服务器内存使用情况
    linux下查看进程id时用到的命令
    python日志模块的使用
    (二十九)c#Winform自定义控件-文本框(二)-HZHControls
    (二十八)c#Winform自定义控件-文本框(一)-HZHControls
    (二十七)c#Winform自定义控件-多输入窗体-HZHControls
  • 原文地址:https://www.cnblogs.com/Qing-LKY/p/hdu1.html
Copyright © 2011-2022 走看看