Codeforces Round #665 (Div. 2)
A. Distance and Axis
题目大意
给定横轴
O
X
OX
OX,其中
A
A
A点坐标为
x
x
x,每次你可以使得
x
−
1
x-1
x−1或
x
+
1
x+1
x+1,问你操作多少次后满足条件的点
B
B
B存在,其中:
∣
O
B
−
A
B
∣
=
k
|OB - AB| = k
∣OB−AB∣=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−(n−y)∣=k⇒n=2y±k
可以看出此时 n ± k n \pm k n±k为偶数,所以只需将 x x x变成奇数或偶数即可,此时答案为 0 0 0或 1 1 1 -
y
≥
n
y \geq n
y≥n
∣ y − ( y − n ) ∣ = k ⇒ n = k |y - (y - n)| = k \Rightarrow n = k ∣y−(y−n)∣=k⇒n=k
那么此时答案为 ∣ x − k ∣ |x-k| ∣x−k∣
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=bi−aibi ai<bi
解题思路
我们可以把所有的情况列举出来
a i a_{i} ai | b i b_{i} bi | c i c_{i} ci |
---|---|---|
0 | 0 | 0 |
0 | 1 | 0 |
0 | 2 | 0 |
1 | 0 | 0 |
1 | 1 | 0 |
1 | 2 | -2 |
2 | 0 | 0 |
2 | 1 | 2 |
2 | 2 | 0 |
那么我们很显然要将
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,z2−x1−(z1−min(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 n−1条边分别赋权,并且满足以下三个条件:
- 边权大于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=1n−1∑j=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)=Cn−x1Cx1=x×(n−x),以任意节点为根节点做一次 d f s dfs dfs即可
然后将其经过次数排序分别与 p i p_{i} pi一一对应即可
要注意的是若 m > n − 1 m > n - 1 m>n−1则需要将多余的边赋权为 1 1 1,若 m ≤ n − 1 m \leq n-1 m≤n−1需要将最大的那些 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;
}