• Educational Codeforces Round 7


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

    A - Infinite Sequence

    签到题

    B - The Time

    签到题

    C - Not Equal on a Segment

    题意:给一个n个数的数组,然后若干次询问,每次询问一个(l,r,x),问区间[l,r]中有没有不等于x的数,假如有,输出任意一个。

    题解:反过来dp,每个数维护“下一个与其不等或者是数组结尾”的数的位置,那么先看a[l]是不是满足a[l]!=x,若是,则输出,否则a[nxt[l]]就是最近的下一个不是x的位置,这个位置假如超过r,就不存在,否则这个位置是其中一种解。

    D - Optimal Number Permutation

    很有意思的一个构造。

    题意:给一个2n个数的数组,由两个n个数的排列打乱而成。显然[1,n]每个数出现两次,第 (i) 个数两次出现的位置之间的差定义为 (d_i) 最小化 (sumlimits_{i=1}^{n}(n-i)|d_i+i-n|)

    题解:要使得上式最小,是要通过调整每个数两次出现的间隔,构造了好几种以1开头的,发现都可以使得和为0,但是没什么规律:

    n=8
    1x2x3x41234xxxxx
    1626384123485775
    

    构造的思路是使得中间一段是从1开始连续自然数,但是这样不知道具体的规律是什么。

    想了很久,突然想试试反过来,从大到小构造,那么

    n=8
    1357753182468642
    

    非常有规律,显然这样构造也会使得和为0。

    具体实现的时候,用两个deque从大到小两头push,然后再for一遍输出。假如要复杂度严格线性的算法,可以直接1357...这样输出。

    注意是n-1那个中间不需要加东西,n-2那个中间放个n。

    *E - Ants in Leaves

    题意:给一棵树,每个叶子有一只蚂蚁,蚂蚁都往根节点爬,但是除了根节点以外的所有节点同时只能容纳1只蚂蚁。求最短要多少时间才能全部爬到根节点。

    题解:根节点很特殊,那么就先去掉根节点,剩下的一片森林中,每棵树的树根也是每次只能完成1个单位的吞吐,和其他节点相同。然后一个大胆的观察出现了:对该子树下的所有的叶子按深度从小到大排序,然后按这些深度逐个吐出叶子上面的蚂蚁。然后赋值对于i>=2,赋值dep[i]=max(dep[i], dep[i-1]+1),恰好就把整个序列离散化完成了。答案就是离散化之后的各个子树的dep[n]的最大值+1。

    const int MAXN = 5e5;
    vector<int> G[MAXN + 5];
    int dep[MAXN + 5], top;
    
    void dfs(int u, int p, int d) {
        if(G[u].size() == 1)
            dep[++top] = d;
        for(auto &v : G[u]) {
            if(v == p)
                continue;
            dfs(v, u, d + 1);
        }
    }
    
    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].push_back(v);
            G[v].push_back(u);
        }
    
        int ans = 0;
        for(auto &v : G[1]) {
            top = 0;
            dfs(v, 1, 0);
            sort(dep + 1, dep + 1 + top);
            for(int i = 2; i <= top; ++i)
                dep[i] = max(dep[i], dep[i - 1] + 1);
            ans = max(ans, dep[top] + 1);
        }
        printf("%d
    ", ans);
        return;
    }
    

    *F - The Sum of the k-th Powers

    题意:求[1,n]的正整数的k次方和。1<=n<=1e9,0<=k<=1e6。即 (sumlimits_{i=1}^{n}i^k)

    题解:易知k次方和是一个k+1次多项式,求出[1,k+2]个点,可以 (O(k^2)) 插值出k+1次多项式。假如选出的点的横坐标是连续整数,可以 (O(k)) 插值出k+1次多项式。

    详见:拉格朗日插值

    拉格朗日插值: (f(x)=sumlimits_{i=1}^{n}y_ifrac{prodlimits_{j=1,j eq i}^{n}x_j-x}{prodlimits_{j=1,j eq i}^{n}x_j-x_i})

    这个模板传入的参数:需要插值n次多项式,传入x[0]~x[n]共n+1个点(*x就是&x[0]),y同理,求出横坐标为xi的函数值。

    先线性预处理出差的前缀积和后缀积(其实前缀积可以不用存下来,转移的时候直接乘上去就可以,但是后缀积建议存下来,因为不一定可能通过好的办法求出乘法逆元(甚至乘法逆元不存在)),然后就可以直接求出分子的值。分母必定是两个阶乘的乘积,而且其正负号相间。

    例如,假设这一段连续的正整数为:

    ([1,2,3,4,5,6])

    那么分母的值分别是:

    ((2-1)(3-1)(4-1)(5-1)(6-1)=+0!5!)
    ((1-2)(3-2)(4-2)(5-2)(6-2)=-1!4!)
    ((1-3)(2-3)(4-3)(5-3)(6-3)=+2!3!)
    ((1-4)(2-4)(3-4)(5-4)(6-4)=-3!2!)
    ((1-5)(2-5)(3-5)(4-5)(6-5)=+4!5!)
    ((1-6)(2-6)(3-6)(4-6)(5-6)=-5!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 x[1000005], y[1000005];
    ll s1[1000005], s2[1000005];
    ll ifac[1000005];
     
    ll lagrange(int n, ll *x, ll *y, ll xi) {
        ll ans = 0;
        s1[0] = (xi - x[0]) % MOD, s2[n + 1] = 1;
        for (int i = 1; i <= n; i++)
            s1[i] = 1ll * s1[i - 1] * (xi - x[i]) % MOD;
        for (int i = n; i >= 0; i--)
            s2[i] = 1ll * s2[i + 1] * (xi - x[i]) % MOD;
        ifac[0] = ifac[1] = 1;
        for (int i = 2; i <= n; i++)
            ifac[i] = -1ll * MOD / i * ifac[MOD % i] % MOD;
        for (int i = 2; i <= n; i++)
            ifac[i] = 1ll * ifac[i] * ifac[i - 1] % MOD;
        for (int i = 0; i <= n; i++) {
            (ans += 1ll * y[i] * (i == 0 ? 1 : s1[i - 1]) % MOD * s2[i + 1] % MOD
                    * ifac[i] % MOD * (((n - i) & 1) ? -1 : 1) * ifac[n - i] % MOD) %= MOD;
        }
        return (ans + MOD) % MOD;
    }
     
    void test_case() {
        ll n, k;
        scanf("%lld%lld", &n, &k);
        for(int i = 1; i <= k + 2; ++i) {
            x[i] = i;
            y[i] = qpow(i, k);
            y[i] = (y[i] + y[i - 1]) % MOD;
        }
        printf("%lld
    ", lagrange(k + 1, x + 1, y + 1, n));
    }
    
  • 相关阅读:
    敏捷软件开发实践-Code Review Process(转)
    随笔1
    随笔
    随笔
    低级错误
    随笔
    随笔2
    随笔
    这以前写的代码
    蛋疼的vs
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12529360.html
Copyright © 2020-2023  润新知