[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;
}

G Kuroni and Antihype

H Kuroni the Private Tutor

posted @ 2020-03-06 00:37  Hock  阅读(199)  评论(0编辑  收藏  举报