【解题报告】Codeforces Round #359 (Div. 2)

来源:互联网 发布:大数据 信用评分模型 编辑:程序博客网 时间:2024/06/15 06:25

题目链接


A. Free Ice Cream(Codeforces 686A)

思路

维护一个主人公现有的冰淇淋数量,根据输入的操作动态地增减它就好了。因为每次操作的操作数比较大,所以要注意防溢出。

代码

#include <bits/stdc++.h>using namespace std;typedef long long ll;ll n, x, opt, num, cnt;int main() {    scanf("%I64d %I64d\n", &n, &x);    while(n--) {        scanf("%c %I64d\n", &opt, &num);        if(opt == '+') {            x += num;        }        if(opt == '-') {            if(x >= num) {                x -= num;            }            else {                cnt++;            }        }    }    printf("%I64d %I64d\n", x, cnt);    return 0;}

B. Little Robber Girl’s Zoo(Codeforces 686B)

思路

题目中给的操作实际上可以简化成只交换相邻两个数,这样,通过一系列的交换,就可以将操作转化为我们熟悉的交换任意两个数。然后可以用选择排序的算法——每次选择待排序序列中的最小值,将其放在最低位置——对待排序序列排序。这样的复杂度是 O(n2) ,执行相邻交换的次数也是 O(n2) ,都在允许的范围内。注意:对序列进行排序的时候要对其执行相邻位置交换而不是任意位置交换(虽然实质上是任意位置交换)。

代码

#include <bits/stdc++.h>using namespace std;const int maxn = 110;int n, m, idx, a[maxn];void solve(int x, int y) {    for(; y > x; y--) {        swap(a[y-1], a[y]);        printf("%d %d\n", y - 1, y);    }}int main() {    scanf("%d", &n);    for(int i = 1; i <= n; i++) {        scanf("%d", &a[i]);    }    for(int i = 1; i <= n ;i++) {        m = a[i];        idx = i;        for(int j = i + 1; j <= n; j++) {            if(a[j] >= m) {                continue;            }            m = a[j];            idx = j;        }        solve(i, idx);    }    return 0;}

C. Robbers’ watch(Codeforces 686C)

思路

首先要将输入的两个数转化为 7 进制(实际上根据题意两个数要先自减1),设 nm7 进制数的长度为 lnlm (要注意 0 也是有长度的),那么求解这道题的方法就是在搜索符合条件的长为 ln+lm 的解向量(将时和分拼起来)。例如,对于第二组数据而言, [0,5,1] (05:1) 就是一个符合条件的解向量。按理说解向量中的每个值的取值范围都应该是 06 但是当之前每个值得取值都达到上限的时候,当前值得上限就有可能不是 6 。例如, n7 进制为 13654 ,若当前搜索到的解向量为 [1,3,6,5] ,那么下一位数的取值范围就是 04 ,若当前搜索到的解向量为 [1,3,6,4] ,那么下一位数的取值范围就是 06 ,因为即使取到 6 ,解向量代表的 7 进制数也不会超过 n

代码

#include <bits/stdc++.h>using namespace std;int n, m, ln, lm, ans, vis[10];string sn, sm;string seven(int n) {    string ans;    for(; n > 0; n /= 7) {        ans.push_back(n % 7);    }    reverse(ans.begin(), ans.end());    return ans;}// flag表示前cur-1位数是否都取到上限void dfs(int cur, int flag) {    if(cur == ln + lm) {        ans++;        return;    }    int ub = 6;    // 若都取到上限则修改本位枚举的上界    if(flag) {        ub = cur < ln ? sn[cur] : sm[cur-ln];    }    for(int i = 0; i <= ub; i++) {        if(!vis[i]) {            int f = 1;            // 计算下一位的flag            if(cur != ln - 1) {                f = flag && i == ub;            }            vis[i] = 1;            dfs(cur + 1, f);            vis[i] = 0;        }    }}int main() {    cin >> n >> m;    sn = seven(--n);    sm = seven(--m);    ln = max((int)sn.size(), 1);    lm = max((int)sm.size(), 1);    dfs(0, 1);    cout << ans << endl;    return 0;}

D. Kay and Snowflake(Codeforces 686D)

思路

对于一个节点 u 而言,它所代表的子树的重心要么是它自己,要么在它的节点数最多的儿子所代表的子树 t 上。设 u 的节点数最多的子树t的重心为 G(t) ,那么 G(u) 一定在 uG(t) 之间的这条链上。所以我们可以用DFS的方法一边求子树的节点数量,一边找子树的重心。感觉这么做复杂度应该是很高的,但是不知道为什么很多人都是这样过的,而且看不懂官方题解,只好先搁置了。

代码

#include <bits/stdc++.h>using namespace std;const int maxn = 3e5 + 10;int n, q, u, c[maxn], p[maxn], cnt[maxn];vector <int> G[maxn];// c[u]表示u点代表的子树的重心void dfs(int u) {    int maxSon = 0;    cnt[u] = 1;    c[u] = u;    for(int i = 0; i < G[u].size(); i++) {        int v = G[u][i];        dfs(v);        // 动态更新子树节点数量        cnt[u] += cnt[v];        if(cnt[v] > cnt[maxSon]) {            maxSon = v;        }    }    // 如果c[u]不是u的话    if(2 * cnt[maxSon] > cnt[u]) {        int cur = c[maxSon];        // 从儿子的重心开始顺着树链向上寻找c[u]        for(; cnt[u] > 2 * cnt[cur]; cur = p[cur]);        c[u] = cur;    }}int main() {    scanf("%d%d", &n, &q);    for(int i = 2; i <= n; i++) {        scanf("%d", &u);        G[u].push_back(i);        p[i] = u;    }    dfs(1);    while(q--) {        scanf("%d", &u);        printf("%d\n", c[u]);    }    return 0;}

(其他题目略)

0 0
原创粉丝点击