Codeforces Round #665 (Div. 2)解题报告

Codeforces Round #665 (Div. 2)

A. Distance and Axis

题目大意

给定横轴 O X OX OX,其中 A A A点坐标为 x x x,每次你可以使得 x − 1 x-1 x1 x + 1 x+1 x+1,问你操作多少次后满足条件的点 B B B存在,其中:
∣ O B − A B ∣ = k |OB - AB| = k OBAB=k

解题思路

假设 B B B点坐标为 y y y,最终 A A A点坐标为 n n n,则:

  1. y < n y < n y<n
    ∣ y − ( n − y ) ∣ = k ⇒ n = 2 y ± k |y - (n - y)|=k \Rightarrow n=2y\pm k y(ny)=kn=2y±k
    可以看出此时 n ± k n \pm k n±k为偶数,所以只需将 x x x变成奇数或偶数即可,此时答案为 0 0 0 1 1 1
  2. y ≥ n y \geq n yn
    ∣ y − ( y − n ) ∣ = k ⇒ n = k |y - (y - n)| = k \Rightarrow n = k y(yn)=kn=k
    那么此时答案为 ∣ x − k ∣ |x-k| xk
AC代码
#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while(T--) {
        int n, k;
        cin >> n >> k;
        int res = abs(n - k);
        if((n + k) % 2 == 0) {
            int x = (n + k) / 2;
            if(x <= n) res = 0;
        }
        else {
            if(n + k >= 1) {
                int x = (n + k - 1) / 2;
                if(x <= n) res = min(res, 1);
            }
            else {
                int x = (n + k + 1) / 2;
                if(x <= n) res = min(res, 1);
            }
        }
        cout << res << '\n';
    }
    return 0;
}

B. Ternary Sequence

题目大意

定义两个序列 a a a b b b,其中 a a a序列中有 x 1 x_1 x1 0 0 0 y 1 y_1 y1 1 1 1 z 1 z_1 z1 2 2 2,同理, b b b中有 x 2 x_2 x2 0 0 0 y 2 y_2 y2 1 1 1 z 2 z_2 z2 2 2 2,现在让你重新排列 a a a b b b使得 ∑ i = 1 n c i \sum_{i=1}^{n}c_{i} i=1nci最大,其中 c i c_{i} ci其中定义如下:
c i = { a i b i   a i > b i 0   a i = b i − a i b i    a i < b i c_{i}=\begin{cases} a_{i}b_{i} \qquad \ a_{i} > b_{i} \\ 0 \qquad \quad \ a_{i}=b_{i} \\ -a_{i}b_{i} \quad \ \ a_{i} < b_{i} \end{cases} ci=aibi ai>bi0 ai=biaibi  ai<bi

解题思路

我们可以把所有的情况列举出来

a i a_{i} ai b i b_{i} bi c i c_{i} ci
000
010
020
100
110
12-2
200
212
220

那么我们很显然要将 a a a中的 2 2 2尽量与 b b b中的 1 1 1进行匹配,将 b b b中的 2 2 2尽量与 a a a中匹配后剩余的 2 2 2 0 0 0进行匹配,那么答案即为:
2 × m i n ( z 1 , y 2 ) − 2 × m a x ( 0 , z 2 − x 1 − ( z 1 − m i n ( z 1 , y 2 ) ) ) 2 \times min(z_1, y_2) - 2 \times max(0, z_2-x_1-(z_1-min(z_1,y_2))) 2×min(z1,y2)2×max(0,z2x1(z1min(z1,y2)))

AC代码
#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while(T--) {
        int x1, y1, z1;
        int x2, y2, z2;
        cin >> x1 >> y1 >> z1;
        cin >> x2 >> y2 >> z2;
        cout << 2 * min(z1, y2) - 2 * max(0, z2 - x1 - (z1 - min(z1, y2))) << '\n';
    }
    return 0;
}

C. Mere Array

题目大意

给定一个序列 a a a,若 g c d ( a i , a j ) = m i n i = 1 n a i gcd(a_{i},a_{j})=min_{i=1}^{n}a_{i} gcd(ai,aj)=mini=1nai,那么你可以将 a i a_{i} ai a j a_{j} aj进行交换,问你经过若干次操作后是否能将 a a a序列变为一个非递减序列

解题思路

模拟发现对于任意两个最小值的倍数之间可以通过最小值作为中转站进行交换,而非最小值的倍数的位置无法改变,故将其倍数挑选出来排序后 O ( n ) O(n) O(n)遍历判断其是否为非递减序列即可

AC代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int Min = INT_MAX;
int a[maxn], d[maxn];
struct Node {
    int w, id;
    Node() { w = id = 0; }
    Node(int ww, int iid) {
        w = ww, id = iid;
    }
    bool operator< (const Node& obj) {
        if(w != obj.w) return w < obj.w;
        return id < obj.id;
    }
}b[maxn], c[maxn];
bool check(int n) {
    for(int i = 1; i <= n; ++i) {
        if(d[i] < d[i - 1]) return false;
    }
    return true;
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while(T--) {
        int n;
        cin >> n;
        Min = INT_MAX;
        for(int i = 1; i <= n; ++i) {
            cin >> a[i];
            Min = min(a[i], Min);
            d[i] = 0;
        }
        int cntb = 0, cntc = 0;
        for(int i = 1; i <= n; ++i) {
            if(a[i] % Min != 0) b[++cntb] = Node(a[i], i);
            else c[++cntc] = Node(a[i], i);
        }
        sort(c + 1, c + cntc + 1);
        for(int i = 1; i <= cntb; ++i) {
            d[b[i].id] = b[i].w;
        }
        int cnt = 0;
        for(int i = 1; i <= n; ++i) {
            if(d[i]) continue;
            d[i] = c[++cnt].w;
        }
        if(check(n)) cout << "YES" << '\n';
        else cout << "NO" << '\n';
    }
    return 0;
}

D. Maximum Distributed Tree

题目大意

给定 n n n个节点构成的一颗树,其各个边权值未知,现在然你为其 n − 1 n-1 n1条边分别赋权,并且满足以下三个条件:

  1. 边权大于0
  2. 所有边权的乘积为 k k k
  3. 权值为 1 1 1的边要尽量少

定义 f ( u , v ) f(u,v) f(u,v)为树上节点 u u u v v v的路径和,并给定 k k k的所有质数因子 p i p_i pi,即 k = ∏ i = 1 m p i k=\prod_{i=1}^{m}p_{i} k=i=1mpi,问你 ∑ i = 1 n − 1 ∑ j = i + 1 n f ( i , j ) \sum_{i=1}^{n-1}\sum_{j=i+1}^{n}f(i,j) i=1n1j=i+1nf(i,j)的最大值是多少

结果对 1 e 9 + 7 1e^{9}+7 1e9+7取模

解题思路

模拟发现,我们只需要求出每条边 ( u , v ) (u,v) (u,v)被经过的次数即可,假设现将边 ( u , v ) (u,v) (u,v)断开,那么该数被切割成两个子树,假设 v v v所在子树节点数为 x x x,总节点数为 n n n,那么边 ( u , v ) (u,v) (u,v)被经过的次数 n u m ( u , v ) = C n − x 1 C x 1 = x × ( n − x ) num_{(u,v)}=C_{n-x}^{1}C_{x}^{1}=x \times (n-x) num(u,v)=Cnx1Cx1=x×(nx),以任意节点为根节点做一次 d f s dfs dfs即可

然后将其经过次数排序分别与 p i p_{i} pi一一对应即可

要注意的是若 m > n − 1 m > n - 1 m>n1则需要将多余的边赋权为 1 1 1,若 m ≤ n − 1 m \leq n-1 mn1需要将最大的那些 p i p_{i} pi乘起来与经过次数最多的边对应

AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const int maxn = 1e5 + 10;
ll a[maxn], b[maxn];
int sz[maxn], n, cnt;
vector<int> v[maxn];
void dfs(int u, int fa) {
    sz[u] = 1;
    int len = v[u].size();
    for(int i = 0; i < len; ++i) {
        if(v[u][i] == fa) continue;
        dfs(v[u][i], u);
        sz[u] += sz[v[u][i]];
        b[++cnt] = sz[v[u][i]] * 1ll * (n - sz[v[u][i]]);
    }
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while(T--) {
        cnt = 0;
        cin >> n;
        for(int i = 1; i <= n; ++i) v[i].clear();
        for(int i = 1; i < n; ++i) {
            int x, y;
            cin >> x >> y;
            v[x].push_back(y);
            v[y].push_back(x);
            a[i] = 1;
        }
        int m;
        cin >> m;
        for(int i = 1; i <= m; ++i) cin >> a[i];
        dfs(1, 0);
        if(m > n - 1) {
            sort(a + 1, a + m + 1);
            for(int i = n; i <= m; ++i) {
                a[n - 1] = a[n - 1] * a[i] % mod;
            }
            reverse(a + 1, a + n);
        }
        else sort(a + 1, a + n, greater<ll>());
        ll res = 0;
        sort(b + 1, b + n, greater<ll>());
        for(int i = 1; i < n; ++i) {
            res = (res + a[i] * (b[i] % mod) % mod) % mod;
        }
        cout << res << '\n';
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值