• atcoder arc111 E


    原题链接:E - Simple Math 3

    题目大意

    给定(A,B,C,D),求满足如下条件的正整数(i)的个数:对于任意的正整数(xin[A + B*i,A + C*i])都有(D mid 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)的倍数的,所以只需要仿照分块时找下标的方式写成(leftlfloor dfrac{l}{D} ight floor == leftlfloor dfrac{r}{D} ight floor)等价于内部没有(D)的倍数.

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

    答案即(res = k - sumlimits_{i=1}^k (leftlfloor dfrac{A+C*i}{D} ight floor - leftlfloor dfrac{A+B*i-1}{D} ight floor)).这要求我们在(O(log(D)))的时间内求出形如(sumlimits_{i=0}^nleftlfloor dfrac{a+bi}{c} ight floor)的式子,这部分是类欧几里得算法的板子,参考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
    ",res);
        }
        return 0;
    }
    
  • 相关阅读:
    redhat 找不到eth0等网卡设备
    oracle12.2 windows 安装卸载重新学习
    Ubuntu官方推荐U盘镜像写入工具(转载别人的)
    linux sh 脚本获取命令结果变变量然后post
    img图片过长自动截取隐藏
    python 数字类型
    Pycharm 常用快捷键 转自 暮良文王
    #python输入输出
    勤学如春起之苗,不见其增日有所长。缀学如磨刀之石,不见其损日有所亏
    感冒了,好难受
  • 原文地址:https://www.cnblogs.com/HotPants/p/14262564.html
Copyright © 2020-2023  润新知