• Ozon Tech Challenge 2020 (Div.1 + Div.2, Rated, T-shirts + prizes!)


    题目链接:https://codeforces.com/contest/1305

    A - Kuroni and the Gifts

    诚心诚意的签到题。

    B - Kuroni and Simple Strings

    题意:给一个括号串s。定义一个括号串是“简单括号串”,当且仅当其是"(((..("+")))...)"的形式,且前后两部分的长度相等。每次操作可以从括号串s中选择一个子序列,要求这个子序列是一个简单括号串,然后把整个子序列从s中删除。要求使用尽可能少的操作使得无法再操作。

    题解:假如某个'('的右边还有')',那么显然还可以继续操作。假如每次只能消除一对'('和')',那么消除最左侧的'('和最右侧的')'是最好的,根据这个思路可以依次把最左和最右的括号匹配掉,当两边指针相遇之后就不会再有匹配的括号了。

    char s[1005];
    int ans[1005], atop;
     
    void test_case() {
        scanf("%s", s + 1);
        int n = strlen(s + 1);
        int firstleft = -1, firstright = -1;
        for(int i = 1; i <= n; ++i) {
            if(s[i] == '(') {
                firstleft = i;
                break;
            }
        }
        if(firstleft == -1) {
            puts("0");
            return;
        }
        for(int i = firstleft + 1; i <= n; ++i) {
            if(s[i] == ')') {
                firstright = i;
                break;
            }
        }
        if(firstright == -1) {
            puts("0");
            return;
        }
        puts("1");
        int L = 1, R = n;
        atop = 0;
        while(L <= R) {
            if(s[L] == ')') {
                ++L;
                continue;
            }
            if(s[R] == '(') {
                --R;
                continue;
            }
            assert(L < R && s[L] == '(' && s[R] == ')');
            ans[++atop] = L;
            ans[++atop] = R;
            ++L;
            --R;
        }
        sort(ans + 1, ans + 1 + atop);
        printf("%d
    ", atop);
        for(int i = 1; i <= atop; ++i)
            printf("%d%c", ans[i], " 
    "[i == atop]);
    }
    

    C - Kuroni and Impossible Calculation

    题意:给一个n(<=2e5)个非负整数的序列,以及正整数m(<=1000),求 (prod_{1leq i < j leq n} |a_i-a_j| mod m) 的值。

    题解:由于m比较小,所以有一些暴力的做法。假如n>=m+1,那么根据鸽巢原理答案就是0,否则n也会很小。n很小直接n^2计算即可。注意 (|a-b|mod m eq |a mod m - b mod m|) ,这个要分类讨论。

    int n, m;
    int a[200005];
     
    void test_case() {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        if(n >= m + 1) {
            puts("0");
            return;
        }
        sort(a + 1, a + 1 + n);
        ll res = 1;
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j < i; ++j) {
                res *= a[i] - a[j];
                if(res >= m)
                    res %= m;
            }
        }
        printf("%lld
    ", res);
    }
    

    D - Kuroni and the Celebration

    题意:给一棵n个节点的有根树,但是根未知,然后每次询问一对(u,v),评测机返回他们的LCA。使用不超过 (lfloor frac{n}{2} floor) 次询问确定根的编号。

    题解:可以观察出,每次询问两个点(u,v),记得到的LCA为w。那么从w向u走和从w向v走的整棵子树都没用了。但是假如w是u和v其中之一,则只能去掉一棵子树,在极端情况下这棵子树只拥有一个节点,这样会导致询问超限。假如每次选的u和v都是叶子,那么w是u和v其中之一的情况,就可以立即确定其是根,否则一定可以去掉至少两个节点。也就是说一次询问至少去掉两个节点。易知最后的边界,会是一棵2个节点的树或者1个节点的树,假如把度数<=1的都叫做叶子,那么有可能会把1个节点的树的这个节点入队两次,把这个情况去掉,或者规定度数=1的才是叶子。

    int n, rt;
    set<int> G[1005];
    int vis[1005];
     
    queue<int> leaf;
     
    int ask(int u, int v) {
        printf("? %d %d
    ", u, v);
        fflush(stdout);
        int w;
        scanf("%d", &w);
        if(w == u || w == v) {
            printf("! %d", w);
            fflush(stdout);
            exit(0);
        }
        return w;
    }
     
    void test_case() {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n - 1; ++i) {
            int u, v;
            scanf("%d%d", &u, &v);
            G[u].insert(v);
            G[v].insert(u);
        }
        for(int i = 1; i <= n; ++i) {
            if(G[i].size() <= 1) {
                leaf.push(i);
                vis[i] = 1;
            }
        }
        while(leaf.size() >= 2) {
            int u = leaf.front();
            leaf.pop();
            int v = leaf.front();
            leaf.pop();
            int w = ask(u, v);
            for(auto &eu : G[u]) {
                G[eu].erase(u);
                if(vis[eu] == 0 && G[eu].size() <= 1) {
                    leaf.push(eu);
                    vis[eu] = 1;
                }
            }
            for(auto &ev : G[v]) {
                G[ev].erase(v);
                if(vis[ev] == 0 && G[ev].size() <= 1) {
                    leaf.push(ev);
                    vis[ev] = 1;
                }
            }
        }
        assert(leaf.size() == 1);
        int r = leaf.front();
        printf("! %d", r);
        fflush(stdout);
        exit(0);
    }
    

    E - Kuroni and the Score Distribution

    显然123456...x这样构造可以获得最大的平衡度,易知用完5000个数的平衡度都不会超过5000*5000。不少于m个平衡度是很好做的,但是比赛的时候不知道怎么弄到恰好m个平衡度,其实只需要调节最后一个x的大小,很明显x每增加1就会失去一个平衡度。这样总是有一个位置是刚刚好的,大概这最后一个数会在10000附近。假如还有剩下的数,就从1e6开始,每隔20000放一个就可以了。

    F - Kuroni and the Punishment

    题意:有n(<=2e5)个1e12量级的正整数,使用最少的操作使得所有数的gcd不为1。单次操作可以对一个数进行+1,或者对一个数进行-1(不能减到0)。

    3月4日凌晨看了RoundGod的解法,觉得随机做法太搞了,在想有没有确定性的做法?后面分析了一下觉得随机做法确实是对的,这个算法的失败概率低得离谱。

    题解:

    首先。只考虑n=2e5的最坏情况,易知答案不会超过2e5,因为把所有的奇数都变成偶数是一种结果。

    随机挑选1个数x,检查x-1,x,x+1三个数的质因数(注意不要重复检查),一共挑选20个数x进行检查即可。若答案需要的gcd不含有上面的这一大堆质因数,那么就会出错,但是概率是多大呢?从上面的过程可知,选出的20个数都至少要走2步才能到达这个gcd,因为答案不超过2e5,其中要走超过2步的数不可能超过1/2,因为一个走2步至少要配一个走0步,这样才有可能比全部变成偶数要好。也就是说失败的概率不超过 ({frac{1}{2}}^{20}=9.53e-7) (大概是百万分之一,是中双色球概率的20倍)。算法的复杂度为60次1e12量级的质因数分解(忽略不计),大概最多有600多种不同的质因数,单次check虽然说最坏是2e5但是大部分会在中途被剪枝(能分解出600多种质因数,那么这些数之间几乎是没有任何关联,这样全部变成偶数就是最好的结果,很多质因数在check的一小半就会被break掉,因为这些数毫无关联,所以>=19的质因数很快就累计够2e5了。)

    int p[1000005], ptop;
    bool pn[1000005];
    
    void sieve() {
        int n = 1000000;
        for(int i = 2; i <= n; ++i) {
            if(!pn[i])
                p[++ptop] = i;
            for(int j = 1; j <= ptop; ++j) {
                int t = i * p[j];
                if(t > n)
                    break;
                pn[t] = 1;
                if(i % p[j] == 0)
                    break;
            }
        }
    }
    
    ll a[200005];
    set<ll> s;
    
    void solve(ll x) {
        if(x <= 1)
            return;
        for(int i = 1; i <= ptop && 1ll * p[i]*p[i] <= x; ++i) {
            if(x % p[i] == 0) {
                s.insert(p[i]);
                while(x % p[i] == 0)
                    x /= p[i];
            }
        }
        if(x != 1)
            s.insert(x);
    }
    
    ll dis(ll x, ll y) {
        if(x <= y)
            return y - x;
        ll t = x % y;
        return min(t, y - t);
    }
    
    void test_case() {
        sieve();
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%lld", &a[i]);
        srand(time(nullptr));
        random_shuffle(a + 1, a + 1 + n);
        int t = min(20, n);
        for(int i = 1; i <= t; ++i) {
            solve(a[i] - 1);
            solve(a[i]);
            solve(a[i] + 1);
        }
        ll ans = n;
        for(auto &e : s) {
            ll tmp = 0;
            for(int i = 1; i <= n; ++i) {
                tmp += dis(a[i], e);
                if(tmp >= ans)
                    break;
            }
            if(ans > tmp)
                ans = tmp;
        }
        printf("%lld
    ", ans);
    }
    

    这鬼东西跑得还真是飞快,甚至可以多check几个,假如设置为[x-2,x+2],也是check20个,出错概率应该减少到了 ((frac{1}{3})^{20}=2.86e-10) ,也是飞快得难以想象,看来是高估了质因数的种类数以及低估了check的效率。

  • 相关阅读:
    hdu 2066 一个人的旅行
    hdu 3790 最短路径问题(迪杰斯特拉)
    hdu 2544 最短路
    hdu 1548 A strange lift(迪杰斯特拉,邻接表)
    hdu 1035 Robot Motion
    hdu 1032 The 3n + 1 problem
    hdu 1031 Design T-Shirt
    hdu 1030 Delta-wave
    hdu1231(最大连续子序列)
    hdu1423(最长公共递增子序列)
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12406247.html
Copyright © 2020-2023  润新知