Codeforces Round #625 (Div. 2) A~E 题解

A. Contest for Robots

  • 题意
    给你一个做题序列 r r r和一个做题序列 b b b,其中 r i r_i ri表示第 i i i题是否正确。问怎么分配题目的分数,使得题目最高分最小,且第一个做题序列的得分比第二个多。

  • 解题思路
    两个做题序列相同的位置肯定是不考虑的,因为再怎么分配题目的分数,两个人得分相同。关键在于第一个序列和第二个序列做题情况不相同的位置。如果要使得第一个序列分高,那么 r i = 0 , b i = 1 r_i=0,b_i=1 ri=0,bi=1的位置的题目得分均设置为 1 1 1,那么实际上就是要使得 r i = 1 , b i = 0 r_i=1,b_i=0 ri=1,bi=0的所有位置得分总和的平均分 x × c n t 1 > c n t 2 x \times cnt1>cnt2 x×cnt1>cnt2 c n t 1 cnt1 cnt1表示 r i = 1 , b i = 0 r_i=1,b_i=0 ri=1,bi=0的数量, c n t 2 cnt2 cnt2表示 r i = 0 , b i = 1 r_i=0,b_i=1 ri=0,bi=1的数量)。故易得 x = c n t 2 / c n t 1 + 1 x = cnt2 /cnt1+1 x=cnt2/cnt1+1。注意特判 c n t 1 = 0 cnt1= 0 cnt1=0的时候,此时无解。

  • AC代码

/**
  *@filename:A
  *@author: pursuit
  *@created: 2021-09-03 09:47
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int n, r[N], b[N];
void solve(){
    //bi存在ri不存在的都设1.
    int cnt1 = 0, cnt2 = 0;
    for(int i = 1; i <= n; ++ i){
        if(b[i] > r[i]){
            ++ cnt1;
        }
        else if(r[i] > b[i]){
            ++ cnt2;
        }
    }
    if(cnt2 == 0){
        puts("-1");
    }
    else{
        int temp = cnt1 / cnt2 + 1;
        printf("%d\n", temp);
    }
}
int main(){	
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i){
        scanf("%d", &r[i]);
    }
    for(int i = 1; i <= n; ++ i){
        scanf("%d", &b[i]);
    }
    solve();
    return 0;
}

B. Journey Planning

  • 题意
    给定一个序列 b b b,现在要你找到一个位置序列 c c c,使得 c i + 1 > c i c_{i+1}>c_i ci+1>ci,且 c − i + 1 − c i = b c i + 1 − b c i c-{i+1}-c_i=b_{c_{i+1}}-b_{c_i} ci+1ci=bci+1bci,同时这个位置序列的 ∑ b c i \sum b_{ci} bci最大。

  • 解题思路
    对于 j > i j>i j>i,我们需要寻找 b j − b i = j − i b_j-b_i=j-i bjbi=ji,变换一下即得 b j − j = b i − i b_j-j=b_i-i bjj=bii。所以我们可以用 p [ i ] p[i] p[i]来表示值为 b i − i b_i-i bii的最大和。那么自然可得状态转移为 p [ b [ j ] − j ] = b j + p [ b [ j ] − j ] p[b[j]-j]=b_j + p[b[j]-j] p[b[j]j]=bj+p[b[j]j]。最后对所有的最优解取最大值即可。

  • AC代码

/**
  *@filename:B
  *@author: pursuit
  *@created: 2021-09-03 09:57
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 2e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int n, b[N];
//选择一个城市作为起点,直到选择某个城市作为结束起点。
//满足j - i = bj -
int dp[N];//表示到达i城市的最美丽度。
map<int, ll> p;//存储b[i] - i的最大美丽度。
void solve(){
    for(int i = 1; i <= n; ++ i){
        p[b[i] - i] = (p[b[i] - i] + b[i]);
    }
    ll res = 0;
    for(auto iter : p){
        res = max(res, iter.second);
    }
    printf("%lld\n", res);
}
int main(){	
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i){
        scanf("%d", &b[i]);
    }
    solve();
    return 0;
}

C. Remove Adjacent

  • 题意
    给定一个字符串。对于第 i i i个位置的字符 c c c,如果它的相邻位置存在字母表中前一个字符,那么就可以删除第 i i i个位置的字符。问你能删除最多的字符。

  • 解题思路
    贪心 + 暴力,我们从zb开始枚举,然后遍历字符串删除符合条件的字符,注意删除完之后我们需要回退到删除位置的前一个字符。

  • AC代码

/**
  *@filename:C
  *@author: pursuit
  *@created: 2021-09-03 10:12
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 100 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int n;
char s[N];
void solve(){
    //枚举选择的起始字符。
    int res = 0;
    for(char c = 'z'; c >= 'b'; -- c){
        for(int i = 1; i <= n; ++ i){
            if(s[i] == c && (i - 1) >= 1 && s[i - 1] == s[i] - 1){
                for(int j = i; j < n; ++ j){
                    s[j] = s[j + 1];
                }
                -- n, i -= 2;
                ++ res;
            }
            if(s[i] == c && (i + 1) <= n && s[i + 1] == s[i] - 1){
                for(int j = i; j < n; ++ j){
                    s[j] = s[j + 1];
                }
                -- n, i -= 2;
                ++ res;
            }

        }
    }
    printf("%d\n", res);
}
int main(){
    scanf("%d%s", &n, s + 1);
    solve();
    return 0;
}

D. Navigation System

  • 题意
    给你n个点,m条无向边,以及一条含n个点得路线。就像地图导航系统,每次在最短路出错就要重新导航,问最小的导航次数和最大的导航次数(最短路可能有多条)。

  • 解题思路
    我们可以反向建边,求出终点 p [ k ] p[k] p[k]到每个点的最短路径。然后我们从 p [ 1 ] p[1] p[1]出发扫描整个路径,如果不符合最短路,说明导航出错,这是一定出错了的,需要重新导航,所以这个时候 c n t 1 + + , c n t 2 + + cnt1++,cnt2++ cnt1++,cnt2++,但如果相等,说明说明当前是最短路,这个时候我们需要考虑是否存在其他的最短路,如果超过 1 1 1条,说明可能导航错误,这个时候 c n t 2 + + cnt2++ cnt2++。至此即可得。

  • AC代码

/**
  *@filename:C
  *@author: pursuit
  *@created: 2021-09-03 10:12
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 2e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int n, m, k;
struct edge{
    int to, next;
} edges1[N << 2], edges2[N];
int head1[N], tot1, head2[N], tot2;
int dist[N];
int p[N];
void add1(int u, int v){
    edges1[++ tot1].to = v;
    edges1[tot1].next = head1[u];
    head1[u] = tot1;
}
void add2(int u, int v){
    edges2[++ tot2].to = v;
    edges2[tot2].next = head2[u];
    head2[u] = tot2;
}
void bfs(){
    queue<int> q;
    dist[p[k]] = 0;
    q.push(p[k]);
    while(!q.empty()){
        int u = q.front();
        q.pop();
        for(int i = head2[u]; i; i = edges2[i].next){
            int v = edges2[i].to;
            if(dist[v] == INF){
                dist[v] = dist[u] + 1;
                q.push(v);
            }
        }
    }
}
void solve(){
    fill(dist, dist + N, INF);
    bfs();
    int cnt1 = 0, cnt2 = 0;
    for(int i = 1; i < k; ++ i){
        int u = p[i];
        if(dist[u] == dist[p[i + 1]] + 1){
            int cnt = 0;
            for(int j = head1[u]; j; j = edges1[j].next){
                int v = edges1[j].to;
                if(dist[u] == dist[v] + 1){
                    ++ cnt;
                }
                if(cnt == 2){
                    ++ cnt2;
                    break;
                }
            }
        }
        else{
            ++ cnt1, ++ cnt2;
        }
    }
    printf("%d %d\n", cnt1, cnt2);
}
int main(){
    scanf("%d%d", &n, &m);
    for(int i = 1, u, v; i <= m; ++ i){
        scanf("%d%d", &u, &v);
        add1(u, v), add2(v, u);
    }
    scanf("%d", &k);
    for(int i = 1; i <= k; ++ i){
        scanf("%d", &p[i]);
    }
    solve();
    return 0;
}

E. World of Darkraft: Battle for Azathoth

  • 题意
    Roma可以购买n种武器中的一种,以及m种不同盔甲套装的一种。其中武器i有攻击修正ai,价值cai币。盔甲套装j有防御值bj,价值为cbj币。选择装备之后可以继续击败一些怪物。他可以尝试击败p个怪物。怪物k有防御值xk,攻击yk,拥有zk币。如果攻击大于防御并且防御大于攻击,则可以击败怪物,大白之后可以拿走所有金币。利润定义为所有击败怪物身上获得的总金币减去他的装备成本。

  • 解题思路
    对于武器和盔甲,我们可以按照其攻击力和防御力排序,因为对于攻击力越高的武器和防御力越高的武器能击败的怪物越多。
    但对于怪物来说,其有两个参数攻击力 y y y和防御力 x x x,我们只能参考一个数值来排序,这里按防御力 x x x来排序。那么对于武器而言,其只需要顺序遍历找出所有小于该攻击力的怪物数量。而这里关键我们怎么选取盔甲。由于怪物的攻击力是无序的,所以我们暴力查找肯定是不行的。那么我们可以处理出有多少怪物的攻击力是能被哪段区间 [ l , r ] [l,r] [l,r]所抵御的。那么这个区间修改操作我们就可以通过线段树来实现了,每个结点保存这段区间最大的利润(没有算上装备的消耗)。
    至此,注意细节处理即可。

  • AC代码

/**
  *@filename:E
  *@author: pursuit
  *@created: 2021-09-03 14:06
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 2e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int n, m, p;//可用武器数,盔甲数以及怪物数量。
struct node1{
    int w, c;
    //按能力值排序。
    node1(int _w = 0){
        w = _w, c = 0;
    }
}a[N], b[N];
ll tree[N << 2], lazy[N << 2];
struct node2{
    int x, y, z;//防御攻击和金币数。
}monster[N];
bool operator < (node1 A, node1 B){
    return A.w < B.w;
}
bool operator < (node2 A, node2 B){
    return A.x < B.x;
}
void buildTree(int rt, int l, int r){
    if(l == r){
        tree[rt] = -b[l].c;//该结点的所需装备的值。
        return;
    }
    int mid = l + r >> 1;
    buildTree(rt << 1, l, mid);
    buildTree(rt << 1 | 1, mid + 1, r);
    tree[rt] = max(tree[rt << 1], tree[rt << 1 | 1]);
}
void pushdown(int rt){
    tree[rt << 1] += lazy[rt], tree[rt << 1 | 1] += lazy[rt];
    lazy[rt << 1] += lazy[rt], lazy[rt << 1 | 1] += lazy[rt];
    lazy[rt] = 0;
}
void update(int rt, int l, int r, int st, int ed, int x){
    if(st <= l && r <= ed){
        //说明范围包含了这个区间。
        tree[rt] += x, lazy[rt] += x;
        return;
    }
    //否则在子树里标记下移。
    pushdown(rt);
    int mid = l + r >> 1;
    if(st <= mid){
        update(rt << 1, l, mid, st, ed, x);
    }
    if(ed > mid){
        update(rt << 1 | 1, mid + 1, r, st, ed, x);
    } 
    tree[rt] = max(tree[rt << 1], tree[rt << 1 | 1]);
}
void solve(){
    sort(a + 1, a + n + 1);
    sort(b + 1, b + m + 1);
    sort(monster + 1, monster + p + 1);
    ll res = -1e17;
    buildTree(1, 1, m);
    for(int i = 1, num = 1; i <= n; ++ i){
        //判断能大于多少的怪物防御。
        while(num <= p && monster[num].x < a[i].w){
            //此时二分出该怪物至少需要多少防御的盔甲才可以。
            int x = upper_bound(b + 1, b + 1 + m, node1(monster[num].y)) - b;
            //cout << x << " " << b[x].w << " " << b[x].c << endl;
            if(x <= m){
                //说明存在。则对这一部分的均进行修改。
                update(1, 1, m, x, m, monster[num].z);
            }
            ++ num;
        }
        res = max(res, tree[1] - a[i].c);
    }
    printf("%lld\n", res);
}
int main(){	
    scanf("%d%d%d", &n, &m, &p);
    for(int i = 1; i <= n; ++ i){
        scanf("%d%d", &a[i].w, &a[i].c);
    }
    for(int i = 1; i <= m; ++ i){
        scanf("%d%d", &b[i].w, &b[i].c);
    }
    for(int i = 1; i <= p; ++ i){
        scanf("%d%d%d", &monster[i].x, &monster[i].y, &monster[i].z);
    }
    solve();
    return 0;
}
posted @ 2022-03-26 16:48  unique_pursuit  阅读(9)  评论(0)    收藏  举报