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

(未完待补)

原文地址:https://www.cnblogs.com/izcat/p/11360920.html