【解题报告】Codeforces Round #405 (rated, Div. 2, based on VK Cup 2017 Round 1)

来源:互联网 发布:国外微博软件 编辑:程序博客网 时间:2024/04/28 15:58

题目链接


A. Bear and Big Brother(Codeforces 791A)

思路

由于 220320 都会超过 106 ,所以只要模拟 20 年两人的体重变化就可以了。当然也可以列方程解之,不过太麻烦。

代码

#include <bits/stdc++.h>using namespace std;typedef long long ll;ll a, b, x, y;int main() {//    freopen("Input3.txt", "r", stdin);    ios::sync_with_stdio(false);    cin.tie(0);    cin >> a >> b;    x = 1;    y = 1;    for(int i = 1; i <= 20; i++) {        x *= 3;        y *= 2;        if(a * x > b * y) {            cout << i << endl;            break;        }    }    return 0;}

B. Bear and Friendship Condition(Codeforces 791B)

思路

这种有“关系”的题目,用图来建模最合适不过。所以问题转化为每个连通分量的子图是否构成“完全图”,也就是两点之间是否都有边相连。完全图有一个性质:其点数 n 与边数 m 满足 m=n×(n1)2 。利用这个性质我们可以用 DFS 的方式(或者 BFS )访问每个连通分量,在每个分量内统计点数和边数。就可以判断该图是否合理了。在算乘法的时候注意防溢出。

代码

#include <bits/stdc++.h>using namespace std;typedef long long ll;const int maxn = 2e5;vector <int> G[maxn];bool vis[maxn];int u, v, n, m;ll V, E;void dfs(int u) {    vis[u] = true;    E += G[u].size();    V++;    for(int v : G[u]) {        if(vis[v] == true) {            continue;        }        dfs(v);    }}int main() {//    freopen("Input1.txt", "r", stdin);    ios::sync_with_stdio(false);    cin.tie(0);    cin >> n >> m;    while(m--) {        cin >> u >> v;        G[u].push_back(v);        G[v].push_back(u);    }    memset(vis, 0, sizeof(vis));    for(int i = 1; i <= n; i++) {        if(vis[i] == true) {            continue;        }        V = E = 0;        dfs(i);        if(V * (V - 1) != E) {            puts("NO");            return 0;        }    }    puts("YES");    return 0;}

C. Bear and Different Names(Codeforces 791C)

思路

本题只需要构造一组解。搜索的话复杂度太高,可以先考虑贪心的思想。首先应当明确,输入中的一个字符串 YESNO 描述的是一个窗口的情况,也就是窗口中的名字是否有重复。
例如 k=4 的某个窗口 [1,1,2,3,4] 是有重复的, k=5 的某个窗口 [3,4,1,5,2] 是没有重复的。注意,这里名字的描述方式是数字,答案要求输出真实人名,我们只需要将数字映射到人名上就行了
如果窗口的描述是 YES 的话那么为了简化问题,构造一个升序序列就行了,例如 [1,2,3,4] ,如果窗口的描述是 NO 的话那么为了简化问题,让窗口首尾的元素相同其它元素保持升序就行了,例如 [1,2,3,1] 。为什么要这么做呢?原因是要为以下的贪心算法做铺垫,下面提出贪心算法:

(注意,下面的构造方法以第一组样例为例)

  1. 先构造前 k1 个元素。将其构造成升序即可。那么我们构造出序列 [1,2]
  2. 然后构造第 k 个元素。检查第 1 个字符串,如果其为 NO 则在序列末尾添加与其窗口起始元素相同的元素,如果其为 YES ,那么添加新的元素即可。在第一组样例中此时的序列变为 [1,2,1]
  3. 重复第 2 个步骤,即不断往序列末尾添加新元素。序列依次被构造成 -> 1,[2,1,2] -> 1,2,[1,2,3] -> 1,2,1,[2,3,4] -> 1,2,1,2,[3,4,5] -> 1,2,1,2,3,[4,5,4]
  4. 最后序列被构造出来了,第一组样例构造出的序列是 1,2,1,2,3,4,5,4 ,将数字映射到姓名即可。比如可以将数字看成 26 进制数然后用英文字幕 A,B,C …表示 1,2,3 …(如下)。

代码

#include <bits/stdc++.h>using namespace std;const int maxn = 100;string s[maxn];int name[maxn];int n, k, cnt;int main() {//    freopen("Input1.txt", "r", stdin);    ios::sync_with_stdio(false);    cin.tie(0);    cin >> n >> k;    for(int i = k; i <= n; i++) {        cin >> s[i];    }    for(int i = 1; i <= k - 1; i++) {        name[i] = ++cnt;    }    for(int i = k; i <= n; i++) {        if(s[i] == "YES") {            name[i] = ++cnt;        }        else {            name[i] = name[i - k + 1];        }    }    for(int i = 1; i <= n; i++) {        cout << (char)(name[i] % 10 + 'A');        cout << (char)(name[i] / 10 + 'a') << ' ';    }    return 0;}

D. Bear and Tree Jumps(Codeforces 791D)

思路

先将问题简化为 k=1 的情况。定义 dis(u,v)u,v 两点之间的距离(假设任意树边的长度为 1 )。那么答案就是

res(1)=dis(u,v) (for all u,v that u<v)

w[u] 为以 u 为根的子树中含有的节点数。那么对于每条树边 (u,v) 我们可以算其对答案 res 的贡献度 w[u]×(nw[u]) 。那么在用 DFS 统计出 w[] 后就可以求出答案

res(1)=(w[u]×(nw[u])) (for all u in the tree)

接着要将问题推广到 k=2,3,4,5 的情况。可不可以直接算 res(1)k 呢?如果这么算的话会发现答案比真正的答案变小了。原因是在按照最多跳k步的规则计算答案时,算的是

ans(k)=dis(u,v)k(for all u,v that u < v)

也就是说

ans(k)=res(1)k+δd=res(1)+Δdk

如果能求出 Δd 的话就能求出结果。

那么我们需要对每条路径算模 k 的余数。当然还是不能一条一条路径算,还是得用贡献度的方法算。设以 u 为根节点的子树为 subtree(u) 。显然我们能够仿照之前求 w[] 数组的方式求 cnt[u][r] ,其表示从 subtree(u) 出发到根节点,有多少条路径的长度模 kr 。然后 subtree(u)Δd 的贡献度是可以计算的(这个贡献度代表以 u 为最近公共祖先所有点对之间的距离对 Δd 的贡献度),设其为 del(subtree(u)) ,那么

Δd=del(subtree(u)) (for all u in the tree)

代码

#include <bits/stdc++.h>using namespace std;typedef long long ll;const int maxn = 2e5 + 5, maxk = 10;vector <int> G[maxn];int n, k, u, v;ll ans, w[maxn], cnt[maxn][maxk];int sub(int a, int b) {    return ((a - b) % k + k) % k;}// 边搜索边统计需要的数据void dfs(int u, int p, int d) {    w[u] = cnt[u][d % k] = 1;    for(int v : G[u]) {        if(v == p) {            continue;        }        dfs(v, u, d + 1);        for(int i = 0; i < k; i++) {            for(int j = 0; j < k; j++) {                // r为余数,k为还要多少能模k余0                int r = sub(i + j, 2 * d);                int need = sub(k, r);                // 计算贡献度                ans += need * cnt[u][i] * cnt[v][j];            }        }        // 从子树总累加cnt[u][i]        for(int i = 0; i < k; i++) {            cnt[u][i] += cnt[v][i];        }        w[u] += w[v];    }    // 计算贡献度    ans += w[u] * (n - w[u]);}int main() {//    freopen("Input2.txt", "r", stdin);    ios::sync_with_stdio(false);    cin.tie(0);    cin >> n >> k;    for(int i = 1; i <= n - 1; i++) {        cin >> u >> v;        G[u].push_back(v);        G[v].push_back(u);    }    dfs(1, -1, 0);    cout << ans / k << endl;    return 0;}

(其它题目略)

0 0
原创粉丝点击