Codeforces Round 858 (Div. 2)
A CF1086A Walking Master
解一个二元一次方程组就行,注意特殊判断无解的情况。
B CF1086B Mex Master
Description
给定一个含 \(n\) 个非负整数的序列 \(a\),求对 \(a\) 重新排序后序列的最小分数。
一个序列 \(b\) 的分数为:不包含在序列 \([b_1+b_2,b_2+b_3,...,b_{n-1}+b_n]\) 中的最小非负整数。
Solution
当序列 \(a\) 中 \(0\) 的个数不大于 \(\lceil n/2 \rceil\) 时,可以将序列排成 \([0,x,0,x,...](x\ne 0)\) 的样子,\(0\)不在该序列中,答案为\(0\) ;
否则,若序列全为 \(0\) ,则答案为 \(1\) ;
否则,或序列中的最大值比 \(1\) 大时,设它为 \(x\),可以将序列排成 \([0,...,0,x,a_i,...,a_k]\),由于\(x>1,a_i\ne0\),故答案为 \(1\) ;
否则,即序列中仅有 \(1\) 和 \(0\),且 \(0\) 的个数比 \(1\) 多,可以将序列排成 \([0,...,0,1,0,1,0,1]\),则答案为\(2\)。
Code
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 2e5 + 5;
int n, a[N], ans = -1;
int main() {
int T;
cin >> T;
while (T--) {
cin >> n;
int ind = 0, x, cnt_0 = 0, maxa = 0;
for (int i = 1;i <= n;++i) {
cin >> x;
if (x == 0) {
++cnt_0;
continue;
}
a[++ind] = x;
maxa = max(maxa, x);
}
if (cnt_0 <= (n + 1) / 2) {
cout << "0" << endl;
continue;
}
if (maxa > 1 || maxa == 0) {
cout << "1" << endl;
continue;
}
cout << "2" << endl;
}
return 0;
}
CF1086C Sequence Master
Description
对于正整数 \(n\) ,构造有 \(2n\) 个元素的序列,满足:从中任意取出 \(n\) 个元素,它们的乘积与剩下 \(n\) 个元素的和相等。
给定长度为 \(2n\) 的序列 \(p\),构造满足上述条件的序列 \(a\),使得 \(\sum_{i=1}^{2n} |a_i-p_i|\) 最小。
只需输出最小的 \(\sum_{i=1}^{2n} |a_i-p_i|\),无需输出构造的序列 \(a\)。
Solution
首先考虑如何构造序列 \(a\)。
注意到,满足条件的 \(a\) 是很少数的。
① 容易发现,\(a_1=a_2=...=a_n=0\) 是一个解,则答案为 \(\sum |p_i|\)
② \(n=1\)时,只需满足 \(a_1=a_2\)即可,易得答案为 \(|p_1-p_2|\)
③ 若所有元素相等且不为 \(0\),即\(a_1=a_2=...=a_n\ne0\),
即需解出 \(na_1=a_1^n \rightarrow n=a_1^{n-1}\) ,仅有一个解:\(n=2,a_1=a_2=a_3=a_4=2\)
④ 若有不等的元素,假设 \(a_1\ne a_2\) ,有:
\(a_1a_3...a_na_{n+1}=a_2+a_{n+2}+...+a_{2n}\) (1)
\(a_2a_3...a_na_{n+1}=a_1+a_{n+2}+...+a_{2n}\) (2)
\(a_1a_2...a_{n-1}a_{n}=a_{n+1}+a_{n+2}+...+a_{2n}\) (3)
(1)-(2)得:\((a_1-a_2)a_3...a_{n+2}=a_2-a_1\)
因为 \(a_1\ne a_2\),所以 \(a_3a_4....a_{n+2}=-1\)
需要满足任从\(\{a_3,a_4,...,a_{2n}\}\)中取 \(n-1\) 个数,它们得乘积都为 \(-1\)
故 \(a_3=a_4=...=a_{2n}\)
注意到只有 \(n\) 为偶数时,这种情况才有解 \(a_3=a_4=...=a_{2n}=-1\) (4)
将(4)代入(1)得:\(a_1+a_2=n-1\) (5)
将(4)代入(3)得:\(a_1a_2=-n\) (6)
联合(5)和(6)解得:\(a_1=n,a_2=-1\) 或 \(a_1=-1,a_2=n\)
综上所述,当$n%2=0 \(时,\){a}$有解 \(\{n,-1,-1,...,-1\}\)
那么接下来只需要确定把 \(n\) 放在序列中的哪个位置即可:放在 \(|p_i+1|-|p_i-n|\)最大的位置。
Conclusion
写下容易得到的特殊解;
对 \(n\) 从小到大进行讨论;
分元素都相等和有不等两种情况讨论:
相等的情况直接解方程,
不等的情况,假设出两个不等的元素,写下等式并进行运算(如相减),解出答案。
注意特殊情况和前提条件。
Code
#include<iostream>
#include<cmath>
#define ll long long
using namespace std;
const int N = 4e5 + 5;
int n, p[N];
ll ans;
ll min(ll x, ll y) {
return x < y ? x : y;
}
int main() {
int T;
cin >> T;
while (T--) {
ans = 0;
cin >> n;
for (int i = 1;i <= n * 2;++i) {
cin >> p[i];
ans += abs(p[i]);
}
if (n == 1) //特殊情况勿漏
ans = min(ans, abs(p[1] - p[2]));
if (n == 2) {
ll tmp_ans = 0;
for (int i = 1;i <= n * 2;++i)
tmp_ans += abs(p[i] - 2);
ans = min(ans, tmp_ans);
}
if (n % 2 == 0) { //清楚前提条件
int pos = 1, val = abs(p[1] + 1) - abs(p[1] - n);
for (int i = 1;i <= n * 2;++i)
if (val < abs(p[i] + 1) - abs(p[i] - n))
pos = i, val = abs(p[i] + 1) - abs(p[i] - n);
ll tmp_ans = 0;
for (int i = 1;i <= n * 2;++i)
if (i == pos) tmp_ans += abs(p[i] - n);
else tmp_ans += abs(p[i] + 1);
ans = min(ans, tmp_ans);
}
cout << ans << endl;
}
return 0;
}
CF1086 D DSU Master
CF1086E Tree Master
Description
给定一棵有 \(n\) 个结点的树,结点标号从 \(1\) 到 \(n\),每个结点 \(i\) 有一个权值 \(i\) 。记结点 \(i\) 的父结点为 \(p_i\) ,树根 \(1\) 的父结点记为 \(0\)。
对于结点 \(x\) 和 \(y\),定义 \(f(x,y)\) 为:
初始 \(ans=0\)
\(while(x\) 和 \(y\) 都不为 \(0\) \()\)
· \(ans\leftarrow ans+a_x*a_y\)
· \(x\leftarrow p_x\),\(y\leftarrow p_y\)
\(f(x,y)=ans\)
给出 \(q\) 个询问,对于第 \(i\) 个询问给出的 \(x_i,y_i\),求出 \(f(x_i,y_i)\) 。
Solution
记忆化搜索。
注意用 \(map\) 来记忆化会TLE,需要记录在数组中,使得用\(O(1)\)的时间可以完成一次查询。具体来说,记录 \(rem[x][i]\) 为第 \(x\) 个结点与同层编号(注意这个编号不是结点标号,而是对树的某一层从\(1\)开始的编号)为 \(i\) 的结点的答案。由于空间的限制,我们只能记录某一层的总结点数小于某个值(我设的是\(1000\))时的答案。
可以证明记搜的时间和空间复杂度都是\(O(n\sqrt n)\):【没看懂这个证明,还在研究】
Code
#include<iostream>
#include<cmath>
#include<map>
#include<vector>
#include<cstring>
#define ll long long
using namespace std;
const int N = 1e5 + 5, M = 1000;
int n, q, a[N], p[N], cnt[N], dep[N], num[N];
ll rem[N][M];
vector<int>to[N];
ll calc(int x, int y) {
if (x == 0)
return 0;
int d = dep[x];
if (cnt[d] < M && rem[x][num[y]] != -1)
return rem[x][num[y]];
ll ret = 1ll * a[x] * a[y] + calc(p[x], p[y]); //注意这里的1ll
if (cnt[d] < M)
rem[x][num[y]] = ret;
return ret;
}
void dfs(int u, int depth) {
dep[u] = depth;
num[u] = ++cnt[dep[u]];
for (int v : to[u])
dfs(v, depth + 1);
}
int main() {
cin >> n >> q;
for (int i = 1;i <= n;++i) cin >> a[i];
for (int i = 2;i <= n;++i) {
cin >> p[i];
to[p[i]].push_back(i);
}
dfs(1, 0);
memset(rem, -1, sizeof(rem));
while (q--) {
int x, y;
cin >> x >> y;
cout << calc(x, y) << endl;
}
return 0;
}