2020牛客多校第七场--H--Dividing

2020牛客多校第七场--H--Dividing_第1张图片
题意: 找出满足条件的点对的数量
满足的条件是

(1, k) is always a Legend Tuple, where k is an integer.
if (n, k) is a Legend Tuple, (n + k, k) is also a Legend Tuple.
if (n, k) is a Legend Tuple, (nk, k) is also a Legend Tuple.

画个图会发现是这样的
2020牛客多校第七场--H--Dividing_第2张图片
上述标星的都是满足条件的,标红星的是先从(1,k)到(1 + k, k) 之后再逐渐累加k产生的,黑星是直接(1,k)开始乘于k然后累加k产生的
我刚开始根据这个图推出个公式是这样的2020牛客多校第七场--H--Dividing_第3张图片
但是这个数据范围太大了…直接推会爆
懵逼了好久后,发现这个可以用整除分块来做,我们对于这个图
2020牛客多校第七场--H--Dividing_第4张图片
不考虑红点和最上面一行的话,那么这一列(假设为第 j j j列)的贡献就是 n / j n/j n/j,之后打个表会发现后面有很长一段这个除出来的数是不变的,也就是整除分块,把每一种商都分成一个块一个块的,之后直接用这个块的商乘于块的大小就是这一整段的贡献,而关于红点的贡献,我们只u需要在跑完第一次分块之后,把 n n n减一,然后再跑一次分块就做到了平移的效果,最后加上最上面一行就可以了
需要注意的是,第一列并不存在平移一说,本来就是相邻的,所以需要去重,最后需要减一次第一列的贡献n
注意减法取模的时候注意负数,先加mod再取模

LL n, k;
void Solve(int& kase) {
    scanf("%lld %lld",&n, &k);
    LL last = 1, res = k - n;
    res = (res + mod) % mod;
    for(LL i = 1; i <= k && i <= n; i = last + 1ll) {
        last = n / (n / i);
        if(last > k) {
            last = k;
        }
        LL tmp = 1ll * (last - i + 1) % mod * ((n / i) % mod) % mod;
        tmp %= mod;
        res += tmp;
        res %= mod;
    }
    -- n;
    for(LL i = 1; i <= k && i <= n; i = last + 1ll) {
        last = n / (n / i);
        if(last > k) {
            last = k;
        }
        LL tmp = 1ll * (last - i + 1) % mod * ((n / i) % mod) % mod;
        tmp %= mod;
        res += tmp;
        res %= mod;
    }
    printf("%lld\n", res);
}

你可能感兴趣的:(多校)