AtCoder Beginner Contest 163 (6/6)

比赛链接:Here

AB水题,

C - management

题意:给一棵 \(N(2\le N\le2e5)\)​ 个节点的有根树,求每个节点的儿子数。

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

const int N = 2e5 + 10;
int a[N];
int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n; cin >> n;
    for (int i = 1, x; i < n; ++i)
        cin >> x, a[x] += 1;
    for (int i = 1; i <= n; ++i) cout << a[i] << "\n";
}

D - Sum of Large Numbers

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

数据范围:\(1\le N\le 2e5,1\le K\le N +1\)

思路:因为 \(10^{100}\) 明显很大,所以取 \(K\) 个总和不可能等于取 \(K+1\) 个数的综合,所以只需要枚举取多少个数即可。

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

const int mod = 1e9 + 7;
int ans = 0;
void add(int &x, int y) {
    x += y;
    if (x >= mod) x -= mod;
    if (x < 0) x += mod;
}
int cal(int l, int r) {
    return 1ll * (l + r) * (r - l + 1) / 2 % mod;
}
int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, k;
    cin >> n >> k;
    for (int i = k; i <= n + 1; i += 1)
        add(ans, cal(n + 1 - i, n) - cal(0, i - 1) + 1);
    cout << ans;
}

E - Active Infants

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

数据范围:\(2\le N\le 2e3,1\le A_i\le 1e9\)

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

\(f[l][r]=\max \left(f[l+1][r]+A_{n o w} \times\left|p_{\text {now }}-l\right|, f[l][r-1]+A_{\text {now }} \times\left|p_{\text {now }}-r\right|\right)\)

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; cin >> 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);
    cout << cal(1, 1, n);
    return 0;
}

F - path pass i

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

数据范围:\(1\le c_i\le N\le 2e5\)

题解:把问题转换成不经过这种颜色的简单路径的数量,总数 \(f[N] = N*(N+1)/2\) 减去它即可。

其中不经过颜色 \(i\) 的简单路径的数量为:\(\sum\limits_{u !=v, u !=w, v !=w, c_{u}=c_{v}=i, \forall w \in \operatorname{path}(u, v), c_{u}=i} f[\operatorname{dis}(u, v)-1]_{\circ}\)​​

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; cin >> n;
    for (int i = 1; i <= n; i++) cin >> C[i];
    for (int i = 1, u, v; i < n; i++) {
        cin >> 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);
        cout << ans[i] << "\n";
    }
}
posted @ 2021-09-02 20:35  RioTian  阅读(67)  评论(0编辑  收藏  举报