[CodeForces] Ozon Tech Challenge 2020
A.Kuroni and the Gifts
题意:两个长度为\(n\)的数列,每个数字都不相同,求一种排列方式,使\(a_i+b_i\)和均不相等。
题解:因为每一个\(a_i\)和\(b_i\)都不相同,我们可以对于\(a\)和\(b\)序列排个序,那么显然\(a_{i-1} + b_{i-1} < a_i + b_i\),从而也就保证了\(a_i+b_i\)和均不相等。
B.Kuroni and Simple Strings
题意:括号序列\(S\), 每次可以删去\((), (()), ((()))\)这样的序列(位置不一定连续), 直到不能删为止, 问最少删几次
题解:贪心, 每次操作从左边找一个左括号, 然后右边找个右括号, 然后左边再找, 右边再找, 直到指针移不动为止,。
一次操作结束, 可以证明这样是最优的, 因为一对括号越早删除越好, 越分散两边越好。
C.Kuroni and Impossible Calculation
题意:求\(\prod_{1 \leq i < j \leq n}|a_i - a_j|\) \(mod\) \(m\)
\(n \leq 2*10^5\) \(m \leq 10^3\)
题解:从数据范围来看,\(n \leq 2*10^5\)而\(m \leq 10^3\),发现必有\(a_i\) \(mod\) \(m = a_j\) \(mod\) \(m\)(容斥原理)
那么如果\(m < n\)答案必定为0, 否则我们可以直接\(n^2\)(实际上是\(m^2\))暴力就行。
D. Kuroni and the Celebration
题意:交互题,你可以询问\(\lfloor \frac{n}{2} \rfloor\)次任意两点的\(lca\),求\(rt\)。
题解:我们可以模拟不断删叶子(度数\(=1\)的点)的过程,不断地删叶子那么最后就只剩下根节点了。如果两个度数\(=1\)的点他们的\(lca\)是其中一个点,那么答案就是这个相同的点。
int n, d[N];
vector < int > G[N];
set < int > s;
inline void del (int u) {
for (int i = 0; i < G[u].size (); i ++ ) {
d[G[u][i]] -- ;
if (d[G[u][i]] == 1) s.insert (G[u][i]);
}
}
int main () {
read (n);
for (int i = 1, u, v; i < n; i ++ ) {
read (u); read (v);
G[u].push_back (v);
G[v].push_back (u);
d[u] ++ ; d[v] ++ ;
}
for (int i = 1; i <= n; i ++ ) if (d[i] == 1) s.insert (i);
while (s.size () > 1) {
int u = *s.begin (), v = *(++s.begin ());
printf ("? %d %d\n", u, v); fflush(stdout);
int w; read (w);
if (w == u || w == v) {
printf ("! %d\n", w);
fflush(stdout);
return 0;
}
del (u); del (v);
s.erase (u); s.erase (v);
}
printf ("! %d\n", *s.begin());
return 0;
}
E. Kuroni and the Score Distribution
题意:构造一个递增的序列,值域\(\in [1, 10^9]\),使得\(i < j < k\)满足\(a_i + a_j = a_k\)的数量为\(m\)
题解:首先有个结论就是\(1,2,3,4...n\)这样\((i, j, k)\)的数量总是最多的。
那么我们可以找到最大的\(f_i < m\) 然后再构造\(a_{i+1}\)获得\(m-f_i\)的有序对。
int n, m, f[N], ans[N];
int main () {
read (n); read (m);
for (int i = 3; i <= n; i ++ ) f[i] = f[i - 1] + ((i - 1) >> 1);
if (f[n] < m) {puts ("-1"); return 0;}
for (int i = 1; i <= n; i ++ ) {
if (f[i] < m) printf ("%d ", i);
else {
int k = i - (m - f[i - 1]) * 2;
ans[i] = k + i - 1;
for (int j = n, k = 1e9; j > i; j --, k -= ans[i] + 2) ans[j] = k;
for (int j = i; j <= n; j ++ ) printf ("%d ", ans[j]);
puts (""); return 0;
}
}
return 0;
}
F Kuroni and the Punishment
题意:给定 \(n\)个数。每次可以选择将一个数 \(+1\) 或 \(-1\) 。求至少多少次操作使得整个序列全部元素的 \(\gcd > 1\)
题解:首先我们可以粗略的确定答案的上界,即是数列中奇数的个数\(t\)(将所有的奇数\(+1\),\(\gcd\)就是\(2\))。
我们可以证明操作为2不会超过\((\lceil \frac{t}{2} \rceil)\),那么就有至少\((\lceil \frac{t}{2} \rceil)\)操作次数为1。(反证法证明)
我们考虑随机化,抽到一个数,有\(\frac{1}{2}\)的概率抽到只修改一次的,我们可以枚举\(+1\)或\(-1\)或不变三种情况,那么答案就在这三种里面。
假设我们抽了\(m\)次,那么正确的概率就是\(1-\frac{1}{2^m}\),当\(m\)较大的时候,正确率无限接近于\(1\),由此得到了一个随机化算法。
inline ll calc (ll x) {
ll ret = 0;
for (int i = 0; i < n; i ++ ) {
ret += min (a[i] < x ? x - a[i] : a[i] % x, x - a[i] % x);
if (ret >= t) break;
}
return ret;
}
inline void work (ll x) {
ll en = sqrt (x);
for (ll i = 2; i <= en; i ++ ) {
if (!(x % i) && !vis[i]) chkmin (ans, calc (i)), vis[i] = 1; while (!(x % i)) x /= i;
if (x == 1) break;
}
if (x != 1) chkmin (ans, calc (x));
}
int main () {
read (n);
for (int i = 0; i < n; i ++ ) {read (a[i]); if (a[i] % 2) t ++ ;}
sort (a, a + n);random_shuffle (a, a + n); ans = t;
for (int i = 0; i < min (n, 51); i ++ ) {
work (a[i]); work (a[i] + 1);
if (a[i] > 1) work (a[i] - 1);
}
printf ("%lld\n", ans);
return 0;
}