• 2019牛客暑期多校训练营(第九场)


      牛客多校第九场:https://ac.nowcoder.com/acm/contest/889
      题目列表:
      // 折叠的自闭经历:
     // 今天真是自闭的一场。
    
     // 上来我就先看A题,数学题,题意很明确,斐波那契数的幂求和,项数很大,应该是打表找规律
     // 打了表看不出规律。。。看榜,没人过
     // Yu今天不舒服上洗手间回来了,准备做I题
     // Wu也在看I题,我瞟了两眼,感觉可以把 k=1~N的二进制位个数分别算出来,每一位单独计算然后结果求和
     // Wu继续打表找规律,我开始写数位dp,写了一半不会写,手算验证一下发现不对
     // Yu提醒没把握不写了,让我看B,我推了半天发现就是要解两个一元二次方程,但是常数c+k*p不确定,暴力需要O(p)怎么都要T
     // 想着 b^2-4*(c+k*p)要能开方,搜勾股数,没有找到确定斜边得到两直角边的公式
     // 方程里有x^2%p 感觉要用到二次剩余的知识,实在不会,换题
     // Wu去写D,不知不觉莽了一发,我一看,2^36绝壁T了,让他对sum剪枝又莽一发
     // 我感觉我能写,搜一半存下来再搜一半,写完有bug,让Yu调,他状态不好没调出来
     // Wu这边还在莽,各种优化各种利用性质,都是徒劳
     // 我debug不出来,换E,一推公式出来了,Wu还在推公式,我直接开始并查集写了
     // 样例过了,一交WA,改了ll继续WA,没辙了,验了公式,实在找不到bug
     // 。。。。。。然后没时间了就结束了
     
     // 比赛结束,发了题解,直接看E,跟我想法完全一致,不解
     // 看别人AC代码,看不懂,忽然看到main函数有跳过的操作
     // 想起原来有重复连边的操作,改了一行,AC了。。。
    
     // 不把D AC不吃饭,在dfs2上面调啊调,发现now值一直都没变,原来sta变负数了
     // 左移改成1LL<<k,还是无输出。一直调啊调,发现bag里面怎么搜不出东西来
     // 原来dfs1存反了,bag[val] = state,写反了找你妈啊卡了我那么久
     // 借鉴了别人的思路,找前一半存下来,一改,A了。。。
    
     // F**k
    View Code

     A - The power of Fibonacci

    题意:

    求 ∑(1, n) f[i]^m mod p,其中 f[i] 为斐波那契数,p = 1000000000,n <= 1000000000,m <= 1000。

    题解:

    找规律是不可找出来的。

    p太大,循环节也会很长,存不下来。

    注意到 p = 2^9 * 5^9,可以先求 sum mod 2^9 与 sum mod 5^9 ,这两部分斐波那契数的循环节分别为 768 与 7812500,可以预处理得到,并暴力求 f[i]^m 的和。

    得到同余方程组

    sum = a1 mod 2^9

    sum = a2 mod 5^9

    中国剩余定理可求出 sum % p

    AC代码:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    // mod = 512*1953125
    typedef long long ll;
    const int mod1 = 512;
    const int mod2 = 1953125;
    const int mod = 1000000000;
    const int len1 = 768;
    const int len2 = 7812500;
    ll f2[len1+10];
    ll f5[len2+10];
    ll f[len2+10];
    ll n, m;
    
    void init() {
        f2[1] = f5[1] = f[1] = 1;
        for(int i=2;i<=len1;i++)
            f2[i] = (f2[i-1] + f2[i-2]) % mod1;
        for(int i=2;i<=len2;i++) {
            f5[i] = (f5[i-1] + f5[i-2]) % mod2;
            f[i] = (f[i-1] + f[i-2]) % mod;
        }
    }
    
    ll myPow(ll a, ll n, ll p) {
    //    if(a==0) return 0;
        ll res = 1;
        while(n) {
            if(n&1) res = res * a % p;
            a = a * a % p;
            n >>= 1;
        }
        return res;
    }
    
    ll sum(ll f[], ll k, ll mod) {
        ll res = 0;
        for(int i=1;i<=k;i++) {
            res = (res + myPow(f[i], m, mod)) % mod;
        }
        return res;
    }
    
    void exgcd(ll a, ll b, ll &x, ll &y) {
        if(b==0){ x=1; y=0; return;}
        exgcd(b, a%b, x, y);
        ll tp = x;
        x = y; y = tp - a/b*y;
    }
    
    ll a[2], b[2] = {mod1, mod2};
    ll china() {
        ll ans = 0, x, y;
        ll lcm = mod;
        for(int i=0;i<=1;++i)
        {
            ll tp = lcm/b[i];
            exgcd(tp, b[i], x, y);
            x = (x%b[i] + b[i]) % b[i];
            ans = (ans + tp*x*a[i]) % lcm;
        }
        return (ans + lcm) % lcm;
    }
    
    
    int main() {
        init();
        cin>>n>>m;
        if(n<len2) {
            printf("%lld
    ", sum(f, n, mod));
        } else {
            a[0] = n/len1 %mod1* sum(f2, len1, mod1)%mod1 + sum(f2, n%len1, mod1);
            a[0] %= mod1;
            a[1] = n/len2* sum(f5, len2, mod2)%mod2 + sum(f5, n%len2, mod2);
            a[1] %= mod2;
        //    printf("%lld %lld
    ", a[0], a[1]);
            printf("%lld
    ", china());
        }
        
        return 0;
    }
    View Code

    B - Quadratic equation

    题意:

    已知b,c,p,求两个整数 x,y 满足 x + y mod p = b,x * y mod p = c 。( p = 1000000007)

    题解:

    已知 x + y,设法求得 x - y 就能得到 x,y。

    又 (x - y)^2 = (x + y)^2 - 2xy = b^2 - 4c  ( mod p ),求出模p下的二次剩余方程即可。

    令 d = b^2 - 4c,参考博客,p = 1e9+7 时,属于奇素数下的第一种情况,方程的解为 d^[(p+1)/4]。

    WA点:

    求出了 x^2 = a mod p 的根以后,即为 x - y 的差,x = ( b + x_y) / 2,但不能用除法。2的逆元为 500000004 = (p+1)/2 。 

     AC代码:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int p = 1e9+7;
    typedef long long ll;
    
    ll myPow(ll a, ll b) {
        ll res = 1;
        while(b) {
            if(b&1) res = res * a % p;
            a = a * a % p;
            b >>= 1;
        }
        return res;
    }
    
    int main() {
        int t; cin>>t;
        while(t--) {
            ll sum, c;
            scanf("%lld %lld", &sum, &c);
            
            ll d = ((sum*sum-4*c) % p + p) % p;
            if(d==0) {
                printf("%lld %lld
    ", sum/2, sum/2);
                continue;
            }
    
            ll x_y = myPow(d, (p+1)/4);
            if(x_y*x_y%p != d) {
                puts("-1 -1");
                continue;
            }
            
            ll x = (sum + x_y)*(p+1 >>1) % p;     // (a/2 %p ==> a * 2^-1 % p ==> a*(p+1)/2)
            ll y = (sum - x + p) % p;
            if(x>y) swap(x, y);
            printf("%lld %lld
    ", x, y);
        }
        return 0;
        
    }
    View Code

    D - Knapsack Cryptosystem

    题意:

    有一种背包加密算法,给长度为 n 的数组和它的某个子集的和,那么密码为长度为 n 的01串,即1代表子集的元素在原数组中,0代表不在。输出该序列。

    题解:

    就是求 n 个数中的哪些数和为给定的 s。直接暴搜 O(2^n),n<=36肯定TLE。

    折半枚举。很容易想到枚举一半,再dfs另一半验证 sum - sum1是否在记录中,有的话可以直接输出答案。

    时间复杂度:在map上的插入与查询都是logN,这里N = 2^(n/2),故两次都是O(2^(n/2)*log(2^(n/2))),最终时间复杂度为O(n*2^(n/2))

    我真是傻逼把map键-值插反了。。。

    AC代码:

    #include<iostream>
    #include<cstdio>
    #include<map>
    using namespace std;
    typedef long long ll;
     
    ll a[40], sum;
    int n;
    map<ll, ll> bag;
    bool flag;
     
    void dfs1(int k, ll now, ll sta) {
        if(k>=n/2) {
            bag[now] = sta;
            return;
        }
         
        dfs1(k+1, now, sta);
        dfs1(k+1, now+a[k], sta|(1LL<<k));
    }
     
    void dfs2(int k, ll now, ll sta) {
        if(k>n) {
            map<ll, ll>::iterator it = bag.find(sum-now);
            if(it!=bag.end()) {
                flag = true;
                 
                ll sta1 = it->second;
                for(int i=0;i<n;i++) {
                    if((sta1>>i)&1 || (sta>>i)&1)
                        putchar('1');
                    else
                        putchar('0');
                }
            }
             
            return;
        }
        if(!flag)
            dfs2(k+1, now, sta);
        if(!flag)
            dfs2(k+1, now+a[k], sta|(1LL<<k));
    }
     
    int main() {
        cin>>n>>sum;
        for(int i=0;i<n;i++) {
            cin>>a[i];
        }
        // 处理前一半,保存部分和与状态
        dfs1(0, 0, 0);
        // 搜索后一半
        dfs2(n/2, 0, 0);
        return 0;
    }
    View Code

    E - All men are brothers

    题意:

    开始有 n 个人互相不认识,每一轮让 x,y 两个人认识成为朋友,则他们和他们的朋友都互相认识。给 m 次操作,输出 m + 1 行表示从最开始及每次操作后,这群人中找到 4 个互相不认识的组合有多少种。

    题解:

    一看肯定需要并查集,手推一下,发现记录人群中 2 个互相不认识的组合 C2,每次操作更新相应值即可。

    设现在合并的两群人的集合大小分别为 mm = sz[Find(x)],nn = sz[Find(y)],剩下的人为 k = n - mm - nn,用 C2 - mm*nn 更新C2;

    4 个人互相不认识的组合 C4 = 上一次C4结果 - 新合并的两组人选两个和剩下人中选两个的情况 = C4 - ( mm * nn* (C2 - mm*k - nn*k ) 。

    AC代码:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    typedef long long ll;
    int fa[100010], sz[100010];
     
    int Find(int x) {
        return x==fa[x]?x:Find(fa[x]);
    }
     
    bool Union(int x, int y) {
        int a = Find(x), b = Find(y);
        if(a==b) return false;
        if(sz[b]>sz[a]) {
            fa[a] = b;
            sz[b] += sz[a];
        }
        else {
            fa[b] = a;
            sz[a] += sz[b];
        }
        return true;
    }
     
    int main() {
        int n, m;
        cin>>n>>m;
        for(int i=0;i<=n;i++) sz[i] = 1, fa[i] = i;
        ll C4 = 1LL*n*(n-1)*(n-2)/3/4*(n-3)/2, C2 = 1LL*n*(n-1)/2;
        printf("%lld
    ", C4);
        bool flag = 0;
        while(m--) {
            int x, y;
            scanf("%d %d", &x, &y);
            int mm = sz[Find(x)];
            int nn = sz[Find(y)];
            if(Union(x, y)) { // 没判断是否为同一组,WA到自闭
                C2 -= 1LL*mm*nn;
                ll tmp = C2 - 1LL*mm*(n-mm-nn) - 1LL*nn*(n-mm-nn);
                C4 -= 1LL*mm*nn*tmp;
            }
             
            if(C4==0) flag = true;
            if(flag) puts("0");
            else
                printf("%lld
    ", C4);
        }
         
        return 0;
    }
    View Code

    (未完待补)

  • 相关阅读:
    Floyd_Warshall算法
    Bellman_Ford算法
    深度优先搜索
    广度优先搜索
    贪心算法_活动选择
    动态规划_0-1背包问题
    算法导论_动态规划_最长回文子序列
    算法导论_动态规划_最长公共子序列
    动态规划解决分割问题
    2016 Google中国开发者大会游记
  • 原文地址:https://www.cnblogs.com/izcat/p/11360920.html
Copyright © 2020-2023  润新知