数学专题训练3 数论2

1. J-Product of GCDs_2021牛客暑期多校训练营2 (nowcoder.com)

给定一个集合,求其所有大小为 k k k 的子集的 g c d gcd gcd 之积. 子集的 g c d gcd gcd​ 定义为子集所有元素的最大公因数.

思路很简单,每个质因数都分开考虑. 因为 p p p 不一定是质数所以要求欧拉函数降幂。由于 n ∗ k n*k nk 很小所以可以 O ( n k ) O(nk) O(nk) 递推法求组合数.

这个题卡线性,有一个做法是保存至少含有 p c p^c pc 的数字有多少个,记为 c n t ( p , c ) cnt(p, c) cnt(p,c),然后这样的方案数是 C c n t ( p , c ) k C_{cnt(p, c)}^{k} Ccnt(p,c)k,每个质数把幂次从大到小枚举,容斥一下。这样质数的个数是 n log ⁡ n \frac{n}{\log n} lognn,质数的幂是 log ⁡ n \log n logn​. 因此枚举一遍是 O ( n ) O(n) O(n)

这里放一个超时的板子,希望能够改一改卡过去.

#include<bits/stdc++.h>
using namespace std;
const int N = 80010;
typedef long long ll;
int prime[N], cnt, st[N];
ll C[40][40010], f[N];
int n, k, a[N];
ll phi, ans, P;

typedef pair<int, int> PII;

#define x first
#define y second

vector<PII> divs[N];

ll mod_pow(ll x, ll n, ll mod)
{
    ll res = 1;
    while(n)
    {
        if(n & 1) res = (__int128)res * x % mod;
        x = (__int128)x * x % mod;
        n >>= 1;
    }
    return res;
}

void sieve(int n)
{
    st[1] = 1;
    for(int i = 2; i <= n; i++)
    {
        if(!st[i]) st[i] = i, prime[cnt++] = i;
        for(int j = 0; prime[j] <= n / i; j++)
        {
            st[i * prime[j]] = prime[j];
            if(i % prime[j] == 0) break;
        }
    }
    for(int i = 1; i <= n; i++)
    {
        int m = i;
        while(m > 1)
        {
            ll cnt = 0, p = st[m];
            while(m % p == 0)
            {
                m /= p;
                cnt++;
            }
            divs[i].push_back({p, cnt});
        }
    }
}

ll get_phi(ll n)
{
    ll res = n;
    for(ll i = 2; i <= n / i; i++)
    {
        if(n % i == 0)
        {
            res = res / i * (i - 1);
            while(n % i == 0) n /= i;
        }
    }
    if(n > 1) res = res / n * (n - 1);
    return res;
}

int CNT[N][20];
void solve(ll p)
{
    CNT[p][0] = n;
    for(int i = 1; i < 20; i++) CNT[p][0] -= CNT[p][i];
    if(CNT[p][0] == n) return;

    int remain = n;
    for(int i = 0; i < 20; i++)
    {
        if(remain < k) break;
        ans = (__int128)ans * mod_pow(p, (__int128)i * (f[remain - 1] -
                                        ((remain - CNT[p][i] - 1) < 0 ? 0 : (f[remain - CNT[p][i] - 1])) + phi) % phi, P) % P;
        remain -= CNT[p][i];
    }
}

int main()
{
    sieve(N - 1);
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d%lld", &n, &k, &P);

        ans = 1;
        fill(f, f + n + 1, 0);
        for(int i = 0; i < cnt; i++) fill(CNT[prime[i]], CNT[prime[i]] + 20, 0);
        
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);

        phi = get_phi(P);

        for(int i = 0; i < n; i++)
        {
            int sz = min(i, k);
            for(int j = 0; j <= sz; j++)
            {
                if(j == 0) C[j][i] = 1;
                else C[j][i] = (C[j][i - 1] + C[j - 1][i - 1]) % phi;
            }
        }

        f[k - 1] = 1;
        for(int i = k; i <= n; i++) f[i] = (f[i - 1] + C[k - 1][i]) % phi;

        for(int i = 1; i <= n; i++)
        {
            for(auto p : divs[a[i]]) CNT[p.x][p.y]++;
        }

        for(int i = 0; i < cnt; i++)
        {
            solve(prime[i]);
        }

        printf("%lld\n", ans);
    }
    return 0;
}

2. H-Convolution_2021牛客暑期多校训练营4 (nowcoder.com)

比较难

3. G-Greater Integer, Better LCM_2021牛客暑期多校训练营5 (nowcoder.com)

比较难

4. Pass! - HDU 6956 - Virtual Judge (vjudge.net)

BSGS+卡常

5. I love permutation - HDU 6970 - Virtual Judge (vjudge.net)

6. Problem - 1548B - Codeforces

在一个序列上找到最长的连续子区间,使得存在 m ≥ 2 m \ge 2 m2,子区间内所有数字模 m m m 同余.

如果每个数字模 m m m​ 的值都一样,设模数为 d d d​​,那么说明 a i − d a_i - d aid​ 模 m m m​ 都为 0 0 0​,说明 gcd ⁡ ( a − d , b − d ) = gcd ⁡ ( a − d , ∣ a − b ∣ ) > 1 \gcd(a-d, b-d) =\gcd(a-d, |a - b|) > 1 gcd(ad,bd)=gcd(ad,ab)>1​. 就是在差分数组上找到最长的一段子区间他们的最大公约数不为 1 1 1​. 然后答案就是这个区间的长度加1. 考虑到最大公约数的单调性用双指针即可.

7. Problem - 1547F - Codeforces

给一个序列环 a a a ( a n − 1 a_{n-1} an1 a 0 a_0 a0 相接),每次可以用一个新的序列 b b b 替代,即 b i = gcd ⁡ ( a i , a i + 1     m o d     n ) b_i = \gcd(a_i, a_{i + 1\ \bmod\ n}) bi=gcd(ai,ai+1 mod n),问最少需要替换多少次才能使这个序列的所有数字都是一样的.

观察可以发现,设原始序列所有数字的最大公约数为 d d d,那么所有数字最终都会变成 d d d,并且 a i a_i ai 变化为 d d d 的次数是从 a i a_i ai 开始能够延伸到最短的距离,且当前的子区间的最大公约数为 d d d​. 至于环怎么处理,把这个序列复制一遍接在后面变成之前的两倍长度即可.

8. Problem - 1537D - Codeforces

给一个正整数 n ( n ≤ 1 0 9 ) n(n \le 10 ^ 9) n(n109),两个人轮流玩游戏,每次进行的操作是选 n n n 的一个因数 d d d,要求 d ≠ 1 d \ne 1 d=1 d ≠ n d \ne n d=n. 不能操作的人输掉。问最后谁会赢.

打个表,规律显然。当 n n n 是奇数,或者 n n n 是2的奇数次幂的时候,先手必输。其他情况先手必赢。可以这样子分析:

总共有三种情况:

  1. n n n 是奇数
  2. n n n 是偶数并且不是 2 2 2 的幂
  3. n n n 2 2 2 的幂

第一种情况:如果 n n n 是质数就输了。否则只能减去一个奇数,变为第二种情况.

第二种情况:可以减去一个奇数变成第一种情况,然后对手在操作一步变成第二种情况. 直到丢给对手一个奇质数为止。因此这种情况必赢,而第一种情况必输.

第三种情况,只有将 n n n 减半,才能避免对手进入第二种情况。因此只有当前的幂次是偶数的时候才能赢.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值