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
  • 相关阅读:
    jquery easy ui 学习 (8)basic treegrid
    jquery easy ui 学习 (7) TreeGrid Actions
    jquery easy ui 学习 (6) basic validatebox
    jquery easy ui 学习 (5) windowlayout
    jquery easy ui 学习 (4) window 打开之后 限制操纵后面元素属性
    提示“应用程序无法启动,因为应用程序的并行配置不正确”不能加载 System.Data.SQLite.dll
    visual studio 添加虚线的快捷键
    VS2010打开项目时,出现“已经在解决方案中打开了具有该名称的项目”问题的解决方案
    visual studio 编译时 出现 Files 的值 乱码
    微信 连接被意外关闭
  • 原文地址:https://www.cnblogs.com/wizarderror/p/12416219.html
Copyright © 2011-2022 走看看