zoukankan      html  css  js  c++  java
  • Educational Codeforces Round 88 (Rated for Div. 2)

    https://codeforces.com/contest/1359

    A - Berland Poker

    随便平均分配一下。

    B - New Theatre Square

    比A还水。但是以后要注意,不要尝试节省不必要的空间,能不优化就不优化。

    *C - Mixing Water

    题意:给两种水,各无限杯,热水温度为 (h) ,冷水温度为 (c) ,你只能轮流倒热水和冷水并且必须先倒热水。要求倒尽可能少的杯数使得总温度接近 (t) ,总温度是所有温度的平均,也就是,假如倒了 (x) 杯热水和 (y) 杯冷水,则总温度为 (frac{xh+yc}{x+y})

    题解:显然若 (tleq frac{h+c}{2}) ,则必须倒两杯,否则要么倒两杯,要么倒奇数杯( (k+1) 杯热水和 (k) 杯冷水)。设一开始先倒入一杯热水,则温度为 (h) ,这之后若每次倒入一杯热水和一杯冷水,温度会变为 (frac{(k+1)h+kc}{2k+1}) ,这个式子显然趋向于 (frac{h+c}{2}) ,直觉上感觉到这个应该是单调递减的,不放心可以临项作差验证。既然是单调的那么就可以使用二分、倍增等算法去做,我选择的算法是倍增。我要求保持总的温度始终大于等于 (t) ,那么根据倍增的想法,若往前走 (k) 步后总温度仍然大于等于 (t) 则往前走 (k) 步,否则不走,无论走还是不走下一次都是走 (k/2) 步。但是这个算法在遇到某些数据的时候会出错。无所谓,我们找出一个大概的 (k) ,然后在其附近20个温度找一个最小的就可以了,最后别忘记和只倒两杯比较一下。

    ll h, c, t;
     
    void TestCase() {
        scanf("%lld%lld%lld", &h, &c, &t);
        ll ans = 0;
        for(ll step = 1ll << 20; step >= 1ll; step >>= 1ll) {
            ll k = ans + step;
            if((2ll * k + 1ll)*t <= (k + 1ll)*h + k * c)
                ans = k;
        }
        ll cur = 2ll;
        long double curdif = fabs(((long double)h + (long double)c) / 2.0 - (long double)t);
        for(ll i = max(0ll, ans - 10ll); i <= ans + 10ll; ++i) {
            long double tmp = (long double)((i + 1ll) * h + i * c) / (long double)((2ll * i + 1ll));
            if(fabs(tmp - t) < curdif) {
                curdif = fabs(tmp - t);
                cur = 2ll * i + 1ll;
            }
     
        }
        printf("%lld
    ", cur);
        return;
    }
    

    用了long double,毕竟有绝对值的真的不知道咋比较谁更近。传闻double都有8位十进制精度,那么long double对付这题应该是游刃有余的。

    *D - Yet Another Yet Another Task

    题意:给一个 (n) 个数的数组,这些数字都很小(绝对值不超过30),Alice先选择一段非空连续区间 ([l,r]) ,Bob选择其中最大的一个元素去掉,剩下的是Alice的得分。最大化Alice的得分。

    题解:一开始想了很多算法,感觉就很不合理,比如计算某个元素为最大值时的结果,这样很容易退化。首先假如没有Bob,这道题就是经典的“最大子数组和”。其实这题目要从值域入手,假设我们取到的最大值为 (x) ,则大于 (x) 的都不能包含进来,直接设为负无穷,然后求一次最大子数组和,然后把最大子数组和减去 (x) 并尝试更新答案。这里有个小问题就是这次的最大子数组和未必真的包含 (x) ,但是容易知道这时一定不是最优答案,最优答案一定至少会是在这次的最大子数组和里面的最大元素作为 (x) 的时候更新。

    提示:最大子数组和的解法:设 (dp[i]) 为必须以第 (i) 个位置结尾的最大得分。

    int n;
    int a[100005];
    ll dp[100005];
    
    void TestCase() {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        ll ans = 0;
        for(int x = 30; x > 0; --x) {
            dp[0] = 0;
            for(int i = 1; i <= n; ++i) {
                if(a[i] > x)
                    a[i] = -INF;
                dp[i] = max(0ll, dp[i - 1]) + a[i];
                ans = max(ans, dp[i] - x);
            }
        }
        printf("%lld
    ", ans);
        return;
    }
    

    *E - Modular Stability

    题目:计数题,给一个 (n,k) ,计算满足下面条件的数组的数量:

    1. ([1,n]) 中的 (k) 个不同的整数,且严格升序。
    2. 对任意的 (x) ,这个数组无论按照什么顺序打乱之后,从左到右取模,到最后的结果一样。

    题解:很明显,若选择 (d)(d) 的倍数,那么取模得到的结果最后制约条件只有 (d) 。所以就是 (sumlimits_{d=1}^{n} C_{lfloorfrac{n}{d} floor-1}^{k-1}) 。否则,就一定满足,这 (k) 个数的gcd(记为 (g) )没有被选中,这时一个结果不同的构造是:若是这种情况,则存在 (i>1)(a_i) 不是 (a_1) 的倍数,则令 (x=a_i)(x mod a_1 = a_i mod a_1) ,由于 (a_i) 不是 (a_1) 的倍数所以这个不为0,而第二种情况 (x mod a_i) 为0,不等,但好像这就能说明不对了吗?暂时没有理解。

    写了这个东西来验证猜想,通过了大数据。

    ll qpow(ll x, ll n) {
        ll res = 1;
        while(n) {
            if(n & 1)
                res = res * x % MOD;
            x = x * x % MOD;
            n >>= 1;
        }
        return res;
    }
    
    ll C(ll n, ll m) {
        ll up = 1, down = 1;
        for(int i = 1; i <= m; ++i)
            down = down * i % MOD;
        for(int i = 1; i <= m; ++i)
            up = up * (n - i + 1) % MOD;
        return up * qpow(down, MOD - 2) % MOD;
    }
    
    void TestCase() {
        int n, k;
        scanf("%d%d", &n, &k);
        ll sum = 0;
        for(int i = 1; i <= n; ++i)
            sum += C((n / i) - 1, k - 1);
        sum %= MOD;
        printf("%lld
    ", sum);
        return;
    }
    

    然后这个东西感觉可以用线段树优化。因为每次是一段连续区间的求积。

    struct SegmentTree {
    #define ls (o<<1)
    #define rs (o<<1|1)
        static const int MAXN = 500000;
        ll st[(MAXN << 2) + 5];
     
        void PushUp(int o) {
            st[o] = st[ls] * st[rs] % MOD;
        }
     
        void Build(int o, int l, int r) {
            if(l == r)
                st[o] = l;
            else {
                int m = l + r >> 1;
                Build(ls, l, m);
                Build(rs, m + 1, r);
                PushUp(o);
            }
        }
     
        ll Query(int o, int l, int r, int ql, int qr) {
            if(ql <= l && r <= qr) {
                return st[o];
            } else {
                int m = l + r >> 1;
                ll res = 1;
                if(ql <= m)
                    res = Query(ls, l, m, ql, qr);
                if(qr >= m + 1)
                    res = res * Query(rs, m + 1, r, ql, qr) % MOD;
                return res;
            }
        }
    #undef ls
    #undef rs
    } st;
     
    ll qpow(ll x, ll n) {
        ll res = 1;
        while(n) {
            if(n & 1)
                res = res * x % MOD;
            x = x * x % MOD;
            n >>= 1;
        }
        return res;
    }
     
    ll inv_down;
     
    void calc_inv_down(int m) {
        ll down = 1ll;
        for(int i = 1; i <= m; ++i)
            down = down * i % MOD;
        inv_down = qpow(down, MOD - 2);
        return;
    }
     
    int n, k;
     
    ll C(int _n, int _m) {
        if(_n < _m)
            return 0;
        ll up = st.Query(1, 1, n, _n - _m + 1, _n);
        return up * inv_down % MOD;
    }
     
    void TestCase() {
        scanf("%d%d", &n, &k);
        if(k == 1) {
            printf("%d
    ", n);
            return;
        }
        st.Build(1, 1, n);
        ll sum = 0;
        calc_inv_down(k - 1);
        for(int i = 1; i <= n; ++i)
            sum += C((n / i) - 1, k - 1);
        sum %= MOD;
        printf("%lld
    ", sum);
        return;
    }
    

    然后看一下别人的题解,好像不需要这么复杂,直接粘贴组合数的模板就可以了。要记得(模为质数时的)组合数 (C_n^k)(O(n)) 预处理之后每次回答就是 (O(1)) 的。只有当模不为质数,或者模数不够大时,乘法逆元才有可能不存在,这时才需要用维护幺半群的线段树,其他时候直接使用维护交换群的数据结构就可以。

    const int MAXN = 1e6;
     
    ll inv[MAXN + 5], fac[MAXN + 5], invfac[MAXN + 5];
     
    void init_C(int n) {
        inv[1] = 1;
        for(int i = 2; i <= n; i++)
            inv[i] = inv[MOD % i] * (MOD - MOD / i) % MOD;
        fac[0] = 1, invfac[0] = 1;
        for(int i = 1; i <= n; i++) {
            fac[i] = fac[i - 1] * i % MOD;
            invfac[i] = invfac[i - 1] * inv[i] % MOD;
        }
    }
     
    ll C(ll n, ll m) {
        if(n < m)
            return 0;
        return fac[n] * invfac[n - m] % MOD * invfac[m] % MOD;
    }
     
    int n, k;
     
    void TestCase() {
        scanf("%d%d", &n, &k);
        init_C(n);
        ll sum = 0;
        for(int i = 1; i <= n; ++i)
            sum += C((n / i) - 1, k - 1);
        sum %= MOD;
        printf("%lld
    ", sum);
        return;
    }
    
  • 相关阅读:
    数字图像处理领域算法之高斯平滑
    字符串数组元素反转
    安装visual studio 2008 team 失败
    .NET中的正则表达式 (二)RegexOptions 枚举
    手动备份、还原windows7、office2010激活信息
    [转]蓝牙基带数据传输机理分析
    .NET中的正则表达式 (三)RegexCompilationInfo 类
    Android AVD语言设置
    蓝牙协议栈
    Bluetooth StructureBlueZ
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/13044667.html
Copyright © 2011-2022 走看看