ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Codeforces Round #785 (Div. 2) [D-E] 题解

2022-05-01 18:35:03  阅读:163  来源: 互联网

标签:785 return int 题解 ll times ans Div binom


目录

D. Lost Arithmetic Progression

题目大意

A, B是两个有限长度的等差数列,C是在A,B都出现的元素组成的另一个等差数列。

现在给定B,C的首项\(f_b,f_c\),公差\(d_b,d_c\)和长度\(l_b,l_c\)求解有多少种可能的A。

注意:

  • 当有无穷多个时,输出-1
  • 答案需要对\(1e9 + 7\)进行取模

思路

显然C的公差\(d_c\)是A,B公差的最小公倍数,也就是\(d_c = \text{lcm}(d_a, d_b)\)。

首先讨论什么时候为\(0\)。

  1. B的首项需要小于C的首项:\(f_b \le f_c\)
  2. B的末项需要大于C的末项:\(f_b + d_b \times (l_b -1 ) \ge f_c + d_c\times (l_c - 1)\)
  3. C的公差需要被B的公差整除:\(d_b|d_c , d_c\%d_b =0\)
  4. C的首项需要被B包含(这样后面才一直包含):\((f_c - f_b) \% d_b = 0\)

然后讨论什么时候为\(-1\),无限的情况只和最前和最后有关。

当\(f_c - d_c <f_b\) 或者 \(f_c + l_c \times d_c > f_b + (l_b - 1)\times d_b\)。那么等差数列\(A\)可以无限延伸。

如果等于就不行了,例如:\(f_c - d_c = f_b\),这样\(A\)往左扩展⬅️一定会遇到\(f_b\),所以无法无限延伸。

剩余的我们只需要枚举\(d_a\),保证\(d_a\)是\(d_c\)的因子(factor)且\(\text{lcm}(d_a, d_b) = d_c\)即可,考虑到我们左右都可以进行扩展,分别可以扩展\(\frac{d_c}{d_a}\)次,因此对于每个满足条件的\(d_a\)有\((\frac{d_c}{d_a})^{2}\)种可能的答案。

代码

#include <bits/stdc++.h>

const int mod = 1e9 + 7;
using ll = long long;

ll add(ll a, ll b){
    return (a + b) % mod;
}

ll mul(ll a, ll b){
    return a * b % mod;
}

ll gcd(ll a, ll b){
    while (b^=a^=b^=a%=b);
    return a;
}
ll lcm(ll a, ll b){
    return a * b / gcd(a, b);
}

void solve(){
    ll fb, db, lb, fc, dc, lc;
    std::cin >> fb >> db >> lb;
    std::cin >> fc >> dc >> lc;

    // check 0
    if (fb > fc || fb + db * (lb - 1) < fc + dc * (lc - 1) || dc % db != 0 || (fc - fb) % db != 0){
        std::cout << 0 << "\n";
        return void();
    }

    // check 1
    if (fb > fc - dc || fb + db * (lb - 1) < fc + dc * lc){
        std::cout << -1 << "\n";
        return void();
    }

    ll ans = 0;
    // enumerate factor of dc
    for (ll da = 1; da * da <= dc; ++ da){
        if (dc % da != 0) continue;

        if (lcm(da, db) == dc){
            ll tmp = mul(dc / da, dc / da);
            ans = add(ans, tmp);
        }
        if (da * da != dc && lcm(dc / da, db) == dc){
            ll tmp = mul(da, da);
            ans = add(ans, tmp);
        }
    }
    std::cout << ans << "\n";
}

int main(){
    std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
    int t = 1;
    std::cin >> t;
    while (t--) solve();
    return 0;
}

E. Power or XOR?

题目大意

给你一个长度为\(n\)序列\(\{B\}\)和一个整数\(k\)。存在另一个序列\(\{A\}, A_i = 2^{B_i}\)。存在一个多义表达式\(E= A_1 \and A_2 \and \cdots \and A_n\),保证其中的\(\and\)至少有\(k\)个代表\(\oplus\),其余的代表\(\texttt{power}\)。现在,要求你求解出所有可能的\(E\)的值,并输出他们的异或(\(\oplus\))。需要注意的是,结果可能很大,所以我们需要在结果上取模\(2^{2^{20}}\)。

  • \(1\le n \le 2^{20}, 0 \le k < n\)
  • \(1 \le B_i < 2^{20}\)

思路

Hint1
我们可以发现,A中所有的元素,无论是否取Mod都是2的幂级数。
Hint2
我们只需要分别考虑某个区间全为Power的贡献。
Hint3
由于Module=2^2^20,所以我们最多只需要考虑区间长度为20即可。

依据上面几个提示,我们进一步探究某个区间\([l, r]\)。我们假设区间\([l, r]\)中都是power符号。那么显然,假设这个区间仅存在一次,它对答案的贡献为:\(2^{B_l \times \Pi _{i=l+1}^{r}A_{i}}\)。当\(B_l \times \Pi_{i=l+1}^{r} \ge 2^{20}\)时,它将不再对答案做出贡献,这是因为它会被Module掉,同时也是这个性质保证了我们答案的时间复杂度。

但是, 除了单独的分析任意一个区间所做的贡献,我们还需要考虑这个区间在多少个可能的\(E\)中出现过。据题目大意中知,我们有尚未使用的任意符号\(m = (n-1) - (r-l) - 2/1/0\),以及尚未使用的\(\oplus\)符号\(q = k - 2/1/0\)。其中,\(2/1/0\)分别代表:正常,左右其中一侧在corner,以及两边都在corner。

因此,区间\([l, r]\)出现的次数为:\(cnt = \tbinom{m}{q} + \tbinom{m}{q+1} + \cdots + \tbinom{m}{m}\),这并不太好计算,但是由于我们需要求的是异或,因此我们只关心\(cnt\)的奇偶性。那么问题回到如何快速判断组合数的奇偶性?显然,暴力计算不符合时间要求。

1. Lucas Theorem

我们将简单介绍下,什么是卢卡斯定理(Lucas Theorem),卢卡斯定理主要是用于求解大组合数取模的问题,其中模数\(p\)必须为素数。

\[\binom{n}{m}\bmod p=\binom{\lfloor n/p \rfloor}{\lfloor m/p \rfloor} \cdot \binom{n\bmod p}{m\bmod p} \bmod p \]

边界条件为,当\(m=1\)时,返回\(0\)。现在,我们已经有了卢卡斯定理,我们略过其证明过程(有兴趣的可以参考Oi-wiki,也就是那个链接),利用其快速判断组合数的奇偶性。

2. 组合数判断奇偶性

给定任意的\(x, y\),存在:

\[\binom{y}{x} \bmod 2 = \binom{z}{0} \times \prod_{k_1} \binom{1}{0} \times \prod_{k_2} \binom{1}{1} \times \prod_{k_3} \binom{0}{0} \times \prod_{k} \binom{0}{1} \mod 2 \]

其中只有\(\tbinom{0}{1} = 0\),其余的都为\(1\),因此我们只需要判断是否存在\(\tbinom{0}{1}\)即可,如果存在则为偶数否则为奇数。

考虑到卢卡斯定理求解中,由于\(p=2\),实际上我们在分别对数字\(x,y\)的每一位进行处理,因此当且仅当\(x\)中任何\(bit\)为\(1\)时\(y\)中对应的\(bit\)也为\(1\)。最终,我们可以用位运算的形式简洁的判断组合数\(\binom{y}{x}\)的奇偶性:\((x\& y)=x\)。

// input x, y
if ((x & y) == x) return "奇数";
else return "偶数"

如上,我们已经证明了如何快速计算组合数的奇偶性,现在我们只需要逐步处理即可。

代码

const int N = 1 << 20;
void solve(){
    int n, k;
    std::cin >> n >> k;
    vt<int> b(n);
    rep (i, 0, n) std::cin >> b[i];

    vt<vt<int>> odevity(n + 1, vt<int> (3, -1));
    auto getparity = [&](int nouse, int nooplus){
        int useoplus = k - nooplus;
        if (odevity[nouse][useoplus] == -1){
            int x = 0;
            for (int cur = nooplus; cur <= nouse; ++ cur){
                x ^= ((nouse & cur) == cur);
            }
            odevity[nouse][useoplus] = x;
        }

        return odevity[nouse][useoplus];
    };

    vt<int> ans(N, 0);
    for (int l = 0; l < n; ++ l){
        ll len = 1;
        for (int r = l; r < n; ++ r){
            if (r == l) len *= b[r];
            else {
                if (b[r] > 20) break;
                len <<= b[r];
                if (len > N) break;
            }
            int nouse = n - 1 - (r - l) - 2;
            int nooplus = k - 2;
            if (l == 0) ++ nouse, ++ nooplus;
            if (r == n - 1) ++ nouse, ++ nooplus;
            ans[len] ^= getparity(nouse, nooplus);
        }
    }

    // if ans == 0 can't pop it 
    while (ans.size() > 1 && ans.back() == 0) ans.pop_back();
    std::reverse(all(ans));
    for (auto x: ans) std::cout << x;
    std::cout << "\n";
}

标签:785,return,int,题解,ll,times,ans,Div,binom
来源: https://www.cnblogs.com/Last--Whisper/p/16213569.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有