atcoder arc111 E - Simple Math 3

原题链接:E - Simple Math 3

题目大意

给定\(A,B,C,D\),求满足如下条件的正整数\(i\)的个数:对于任意的正整数\(x\in[A + B*i,A + C*i]\)都有\(D \nmid x\).

数据范围:

\(1 \leq T \leq 10^4\)组数据

\(1 \leq A < D\)

\(0 \leq B < C < D\)

\(2 \leq D \leq 10^8\)

思路

多组测试数据,单组复杂度应该是\(\log(D)\)或者根号的.方向可能就是求得形如下取整的和式再根号的数论分块.

根据鸽巢原理:对于\(i\)形成的区间长度不能达到\(D\)及以上,因为一个含有\(D\)个数的区间的序列所有数关于\(D\)的余数一共有\(D\)种,必然存在一个数使得被\(D\)整除.所以可以求出\(i \leq \dfrac{D - 2}{C - B}\).此后记\(k = \dfrac{D - 2}{C - B}\).那么\(k\)就代表所有可能是答案的区间的个数,接下来需要从中挖掉不符合条件的区间的个数.

考虑一个区间是否存在\(D\)的倍数的条件,由于原来的形式太丑陋了,这里简写成考虑\([l,r]\)区间不存在\(D\)的倍数的条件.一个比较符合直觉的想法是:\(D\)的倍数一定在区间之内,不过我们首先不看极端情况(倍数在区间边缘上),看一下一般的情况,这个时候假设有一个倍数存在于区间内,并且不在端点上,我们不妨把整个序列按\(D\)的倍数在的位置划分段落,再来看\([l,r]\)会发现两个端点一定不属于同一个段落集合,那么反过来只要在一个段落集合里,那么就说明落在了一段以\(D\)倍数划分的段落以内,这个段落以内是不存在\(D\)的倍数的,所以只需要仿照分块时找下标的方式写成\(\left\lfloor \dfrac{l}{D}\right\rfloor == \left\lfloor \dfrac{r}{D}\right\rfloor\)等价于内部没有\(D\)的倍数.

不过这个等式是错的,因为还少了一种情况:当左端点\(l\)恰好是\(D\)的倍数时,同样会满足条件,但是显然这个时候区间是包含了\(D\)的倍数的,这里有个很神棍的解决办法,把左端点往左移动一位,条件换成\(\left\lfloor \dfrac{l - 1}{D}\right\rfloor == \left\lfloor \dfrac{r}{D}\right\rfloor\).正确性在于一开始就保证了所有的区间长度在\(D-1\)之内,而往左过去一位不会使不相同的变得相同,只会让\(l\)是左端点的情况被删掉.

答案即\(res = k - \sum\limits_{i=1}^k (\left\lfloor \dfrac{A+C*i}{D}\right\rfloor - \left\lfloor \dfrac{A+B*i-1}{D}\right\rfloor)\).这要求我们在\(O(log(D))\)的时间内求出形如\(\sum\limits_{i=0}^n\left\lfloor \dfrac{a+bi}{c}\right\rfloor\)的式子,这部分是类欧几里得算法的板子,参考oi-wiki或洛谷.

我的代码具体实现的时候,板子是从\(0\)开始计算的,所以计算结果挖掉了\(i=0\)的情况.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	

ll f(ll a, ll b, ll c, ll n)
{
    if (a == 0)
        return (n + 1) * (b / c);
    if (a >= c || b >= c)
        return (f(a % c, b % c, c, n) + (a / c) * n * (n + 1) / 2 + (b / c) * (n + 1));
    ll m = (a * n + b) / c;
    return (n * m - f(c, c - b - 1, a, m - 1));
}

int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
		ll a,b,c,d;scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
		ll k = (d - 2) / (c - b);
		ll res = k;
		ll lf = f(c,a,d,k) - a / d;
		ll rt = f(b,a - 1,d,k) - (a - 1) / d;
		res -= lf - rt;
		printf("%lld\n",res);
    }
    return 0;
}
posted @ 2021-01-11 15:54  随处可见的阿宅  阅读(203)  评论(0编辑  收藏  举报