AtCoder Beginner Contest 163

Posted zdragon1104

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AtCoder Beginner Contest 163相关的知识,希望对你有一定的参考价值。

传送门

A - Circle Pond

技术图片
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main() {
    int n;
    scanf("%d", &n);
    printf("%.6f
", 2.0 * acos(-1) * n);
    return 0;
}
A.cpp

B - Homework

技术图片
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main() {
    int n, m;
    scanf("%d%d", &m, &n);
    for(int i = 0, x; i < n; i++) {
        scanf("%d", &x);
        m -= x;
    }
    printf("%d
", m < 0 ? -1 : m);
    return 0;
}
B.cpp

C - management

题意:给一棵N个节点的有根树,求每个节点的儿子数。

数据范围:$ 2 leq N leq 2 imes 10^{5} $

题解:由于输入直接给的是每个节点的父节点,直接计数即可。

技术图片
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5 + 5;
int a[N];
int main() {
    int n;
    scanf("%d", &n);
    for(int i = 1, x; i < n; i++) {
        scanf("%d", &x);
        a[x]++;
    }
    for(int i = 1; i <= n; i++) {
        printf("%d
", a[i]);
    }
    return 0;
}
C.cpp

D - Sum of Large Numbers

题意:当前有N + 1个整数:10100,10100+1,...,10100+N,求取不少于K个数的和的可能值的数量(mod 1e9+7)。

数据范围:$ 1 leq N leq 2 imes 10^{5},1 leq K leq N+1 $

题解:10100很大,所以取K个数的总和不可能等于取K+1个数的总和,所以只需要枚举取多少个数。

对于取K个数,可以求出取K个值的最小最大值,在这两个值之间的值都可以取到,个数就是最大值-最小值+1。

技术图片
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MD = 1e9 + 7;
void add(int& x, int y) {
    x += y;
    if(x >= MD) x -= MD;
    if(x < 0) x += MD;
}
int cal(int l, int r) {
    return 1LL * (l + r) * (r - l + 1) / 2 % MD;
}
int main() {
    int n, k, ans = 0;
    scanf("%d%d", &n, &k);
    for(int i = k; i <= n + 1; i++) {
        add(ans, cal(n + 1 - i, n) - cal(0, i - 1) + 1);
    }
    printf("%d
", ans);
    return 0;
}
D.cpp

 E - Active Infants

题意:有N个小孩,第i个孩子的位置为i,活跃值为Ai,现在将N个小孩重新排列,每个小孩获得的开心值为Ai与重新排列前后位置差的乘积,求最大可能的开心值总和。

数据范围:$ 2 leq N leq 2000, 1 leq A_{i} leq 10^{9} $

题解:可以发现将A较大的值放在边上更优,以A降序,然后就是一个区间dp,枚举当前值放左边、右边进行更新。

$ f[l][r] = max(f[l+1][r] + A_{now} imes left |  p_{now} - l ight |,f[l][r-1]+A_{now} imes left |  p_{now} - r ight | )$

技术图片
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e3 + 5;
ll f[N][N];
pair<int, int> p[N];
ll cal(int cnt, int l, int r) {
    if(l > r) return 0;
    if(~f[l][r]) return f[l][r];
    ll ans = 1LL * p[cnt].first * abs(p[cnt].second - l) + cal(cnt + 1, l + 1, r);
    ans = max(ans, 1LL * p[cnt].first * abs(p[cnt].second - r) + cal(cnt + 1, l, r - 1));
    return f[l][r] = ans;
}
int main() {
    int n;
    scanf("%d", &n);
    for(int i = 1, x; i <= n; i++) {
        scanf("%d", &x);
        p[i] = {x, i};
    }
    sort(p + 1, p + n + 1,[](pair<int, int> a, pair<int, int> b) {
        return a.first > b.first;
    });
    memset(f, -1, sizeof f);
    printf("%lld
", cal(1, 1, n));
    return 0;
}
E.cpp

 F - path pass i

题意:给一棵N个节点的无根树,每个节点有一个颜色属性c,对于每个颜色,求经过这种颜色的简单路径的数量。

数据范围:$ 1 leq c_{i} leq N leq 2 imes 10^{5} $

题解:把问题转换成不经过这种颜色的简单路径的数量,总数减去它即可。

其中不经过颜色i的简单路径的数量为:$ sum_{u!=v,u!=w,v!=w,c_{u}=c_{v}=i,forall w   epsilon  path(u,v), c_{w} != i} C_{dis(u,v)-1}^{2} $。

以任意一点为根节点,遍历的时候同时更新相应的值,具体看代码。

技术图片
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5 + 5;
vector<int> G[N];
int C[N], num[N], sum[N];//num[i]代表i子树的节点数目,sum[i]代表以颜色为i的节点(其祖先没有颜色为i的节点)为根节点的子树大小总和
ll ans[N];
ll cal(int x) {
    return 1LL * x * (x + 1) / 2;
}
void dfs(int u, int fa) {
    int c = C[u], save = sum[c];
    num[u] = 1;
    for(auto v : G[u]) {
        if(v == fa) continue;
        int t = sum[c];
        dfs(v, u);
        int dt = sum[c] - t;
        ans[c] -= cal(num[v] - dt);//num[v]-dt代表相邻两个节点之间的节点数
        num[u] += num[v];
    }
    sum[c] = save + num[u];
}
int main() {
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &C[i]);
    }
    for(int i = 1, u, v; i < n; i++) {
        scanf("%d%d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    for(int i = 1; i <= n; i++) {
        ans[i] = cal(n);
    }
    dfs(1, -1);
    for(int i = 1; i <= n; i++) {
        int t = n - sum[i]; //多出来的节点还要减掉
        ans[i] -= cal(t);
        printf("%lld
", ans[i]);
    }
    return 0;
}
F.cpp

 

以上是关于AtCoder Beginner Contest 163的主要内容,如果未能解决你的问题,请参考以下文章

AtCoder Beginner Contest 234

AtCoder Beginner Contest 115 题解

AtCoder Beginner Contest 154 题解

AtCoder Beginner Contest 103

AtCoder Beginner Contest 228

AtCoder Beginner Contest 242