zoukankan      html  css  js  c++  java
  • 牛客练习赛58

    Contest Info


    Practice Link

    SolvedABCDEF
    4/6 O O Ø Ø  -  Ø
    • O 在比赛中通过 
    • Ø 赛后通过
    • ! 尝试了但是失败了
    • - 没有尝试

    Solutions


    C.矩阵消除游戏

    题意:

    有一个$n*m$的矩阵,牛妹可以进行$k$次操作,每次任选一行/列将这列所有元素变为0,并将他们的和纳入自己的得分,要求最大化得分

    思路:

    最开始是想到了贪心,样例能过但是$WA$了,这里给出个反例:

    3  3  2
    100 100 1
    5   10  2
    10  5   5

    后来觉得数据这么小,应该可以枚举,然而一时不知道怎么枚举,赛后补题发现就是自己原来看过的二进制枚举

    分析可以发现假如$kgeqslant min(n, m)$,那么所有直接输出整个矩阵的和就可以了;没有的话就进行二进制枚举,我们可以考虑枚举选择了多少列,再计算每一行中剩余元素的和进行排序

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    int n, m, k, ans, tot;
    int a[20][20], sum[20];
    bool cmp(int x, int y){
        return x > y;
    }
    int main(){
        scanf("%d%d%d", &n, &m, &k);
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++) 
                scanf("%d", &a[i][j]),tot += a[i][j];
        if(k>=min(n,m)) return !printf("%d", tot);
            
        for(int i = 0; i < 1<<m; i++){
            int tmp = i, cnt = 0;
            while(tmp) cnt++, tmp &= (tmp-1);
            if(cnt>k) continue;
            for(int i = 1; i <= n; i++) sum[i] = 0;
            int num = i, res = 0;
            for(int i = 1; i <= n; i++){
                for(int j = 1; j <= m; j++){
                    if(num&(1<<(j-1))) res += a[i][j];
                    else sum[i] += a[i][j]; 
                }
            }
            sort(sum+1, sum+1+n, cmp);
            for(int i = 1; i <= k-cnt; i++) res += sum[i];
            ans = max(ans, res);
        }
        return !printf("%d", ans);
    } 
    View Code

    D.迷宫

    题意:

    有一个$n∗m$的迷宫,迷宫中存在些障碍,牛妹优先按照右、下、左、上的顺序移动,问是否可以添加一些障碍使得牛妹能从格子$(1,1)$走到格子$(n,m)$,如果有则输出最小值,没有输出$-1$

    思路:

    这题我拿到一开始的想法是$dfs$去搜索各个状态,但是莫名其妙样例都没过,写出来也有可能爆了

    其实就是经典的棋盘$dp$问题,只不过有小小的思维量

    认真思考下你会发现,她不可能向左和向上走:

    • 不能向左走:向左走后,所处位置右边的格子会空出来,它又会走回去,在两者间来回跳跃
    • 不能向上走:向上走,说明右、下、左三个格子都是障碍,它只能是由上面走下来的。这说明他上面那个格子不能向右走,它走到上面格子后,只能选择向下,然后上下来回跳跃

    这样问题问题转换为:有一个$n∗m$的迷宫,牛妹只能向右和向下走,但优先向右走,是否可以添加一些障碍使得牛妹能从格子$(1,1)$走到格子$(n,m)$

    我们定义$dp[i][j]$为到达$(i,j)$所需要的最小改变数,则有:

    $dp_{i,j}->dp_{i,j+1}$

    $dp_{i,j}+s[i][j+1]=='0')->dp_{i+1,j}$

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define inf 0x3f3f3f3f
    using namespace std;
    int n, m, dp[1010][1010];
    char s[1010][1010];
    int main(){
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++) scanf("%s", s[i]+1);
        memset(dp, 0x3f, sizeof(dp));
        dp[1][1] = 0;
        for(int i = 1;i <= n; i++){
            for(int j = 1;j <= m; j++){
                if(s[i][j]!='0') continue;
                if(s[i][j+1]=='0'){
                    dp[i][j+1] = min(dp[i][j+1], dp[i][j]);
                    if(s[i+1][j]=='0') dp[i+1][j] = min(dp[i+1][j], dp[i][j] + 1);
                }
                else if(s[i+1][j]=='0') dp[i+1][j] = min(dp[i+1][j], dp[i][j]);
            }
        }
        if(dp[n][m]!=inf) printf("%d", dp[n][m]);
        else printf("-1");
    } 
    View Code

    F.XOR TREE

    题意:

    给定有$n$个节点的树,已知每个节点的权值,完成两种操作:

    1. $(u, x)$ 将点$u$ 的权值修改为$x $
    2. $(u, v)$求点$u $到点$v $之间的所有子路径的长度的异或和(路径长度定义为两点间最短路径上的所有结点的权值的异或和)

    思路:

    这题重点在于如何根据题目给的条件去找到规律方法简化这个问题,而异或一个重要的性质就是异或两次就会相消

    一条长为$k$的路径$v_{1}, v_{2},  dots , v_{k}$,对其中一个点$v_{i}$,我们考虑它的贡献,即在所有子区间中出现的次数,由排列组合可得为$i*(k-i+1)-1$(一个点本身不算路径)

    根据异或的特性,所有出现次数为奇数的点可并入答案中,分析$i*(k+i-1)-1=i*k-i*(i+1)-1$得:

    • 当$k$为偶数,所有位置的次数都是奇数,那么在树上查询就可以了。
    • 当$k$为奇数,那么就是$i$为偶数的位置提供答案,按照树上,那么就隔一个顶点贡献一次

    考虑到次序这个东西可以用深度相邻黑白染色来实现,我们把树拆成两份,黑色一份,白色一份

    黑色树上只记录黑色点的权值,白色点权值全部为零,白色树反之

    最后用树链剖分+线段树实现就好了

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define lson rt<<1, l , mid
    #define rson rt<<1|1, mid+1 , r
    using namespace std;
    const int maxn = 2e5+100;
    int n, q, u, v, op, x, y, a[maxn];
    int cnt, head[maxn];
    int tot, dep[maxn], siz[maxn], id[maxn], f[maxn], son[maxn], top[maxn];
    int sum[2][maxn<<2];
    struct edge{
        int to, nxt;
    }e[maxn<<1];
    void add(int u, int v){
        e[++cnt].nxt = head[u], e[cnt].to = v;
        head[u] = cnt; 
    }
    void dfs1(int u, int fa){
        siz[u] = 1;
        for(int i = head[u]; i; i = e[i].nxt){
            int v = e[i].to;
            if(v==fa) continue;
            dep[v] = dep[u] + 1, f[v] = u;
            dfs1(v, u), siz[u] += siz[v];
            if(siz[v]>siz[son[u]]) son[u] =  v;
        }
    }
    void dfs2(int u, int topf){
        top[u] = topf, id[u] = ++tot;
        if(son[u]) dfs2(son[u], topf);
        for(int i = head[u]; i; i = e[i].nxt){
            int v = e[i].to;
            if(v==son[u]||v==f[u]) continue;
            dfs2(v, v);
        }
    }
    void push_up(int x, int rt){
        sum[x][rt] = sum[x][rt<<1]^sum[x][rt<<1|1];
    }
    void update(int x, int rt, int l, int r, int pos, int val){
        if(l==r){
            sum[x][rt] = val;
            return;
        }
        int mid = (l+r)>>1;
        if(pos<=mid) update(x, lson, pos, val);
        else update(x, rson, pos, val);
        push_up(x, rt);
    }
    int query(int x, int rt, int l, int r, int ql, int qr){
        if(ql<=l&&r<=qr) return sum[x][rt];
        int mid = (l+r)>>1;
        int res = 0;
        if(ql<=mid) res ^= query(x, lson, ql, qr);
        if(qr>mid) res ^= query(x, rson, ql, qr);
        return res;
    }
    int getsum(int x, int u, int v){
        int ans = 0;
        while(top[u]!=top[v]){
            if(dep[top[u]]<dep[top[v]]) swap(u, v);
            ans ^= query(x, 1, 1, n, id[top[u]], id[u]);
            u = f[top[u]];
        }
        if(dep[u]>dep[v]) swap(u, v);
        ans ^= query(x, 1, 1, n, id[u], id[v]);
        return ans;
    }
    int main(){
        scanf("%d%d", &n, &q);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        for(int i = 1; i <= n-1; i++){
            scanf("%d%d", &u, &v);
            add(u, v), add(v, u);
        }
        dfs1(1, 1), dfs2(1, 0);
        for(int i = 1; i <= n; i++) update(dep[i]%2, 1, 1, n, id[i], a[i]);
        while(q--){
            scanf("%d%d%d", &op, &x, &y);
            if(op==1) update(dep[x]%2, 1, 1, n, id[x], y);
            else{
                if(dep[x]%2!=dep[y]%2) printf("%d
    ", getsum(0, x, y)^getsum(1, x, y));
                else printf("%d
    ", getsum((dep[x]%2)^1, x, y)); 
            }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    八款常用的 Python GUI 开发框架推荐
    scrapy + mogoDB 网站爬虫
    tkinter界面卡死的解决办法
    通过核心API启动单个或多个scrapy爬虫
    爬虫怎样绕过验证码?
    Python爬虫入门教程 33-100 电影评论数据抓取 scrapy
    Python开发 之 Websocket 的使用示例
    StringBuffer详解
    其实python正则表达式就这样简单明了
    ABAP和Java里的单例模式攻击
  • 原文地址:https://www.cnblogs.com/wizarderror/p/12416219.html
Copyright © 2011-2022 走看看