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

    科技2024-03-22  88

    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,则:

    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 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} ci00001002010011012-2200212220

    那么我们很显然要将 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条边分别赋权,并且满足以下三个条件:

    边权大于0所有边权的乘积为 k k k权值为 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; }
    Processed: 0.011, SQL: 8