(转载)类欧几里得(知识点整理+板子总结)

本文深入探讨了类欧几里得算法的核心思想及应用,解析了算法如何通过求解特定直线上的整点数量来解决复杂问题。文章涵盖了算法的基本原理、递归转换过程、常见不等式的运用,以及在数位DP和计数问题中的实践案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

思路来源

类欧几里得算法_wwx233的博客-优快云博客

类欧几里得算法 - cyz666 - 博客园

类欧几里得算法总结_DZYO的博客-优快云博客(证明清楚详细)

数数【数位DP||类欧】_以往不谏,来者可追的博客-优快云博客_ztxz16从小立志成为码农,因此一直对数的二进制表示很感兴趣。今天的数学课上,ztxz

心得

一直自己想学,camp回来不会,终于在归神的督促(ruanmoyingpao)下,学会了

知识点整理

类欧可以等价于求y=(ax+b)/c这条直线与x=0和x=n y=0围成的直角梯形上的整点的个数,

注意a、b均非负才可以用,否则就需要继续化简

在直线上的统计在内,在x=0和x=n上的统计在内,在y=0上的不统计

几个常用不等式,在下式推导中起作用,主要变竖向枚举为横向枚举,

i和j互换的时候,交换枚举顺序,相当于把起初求每个竖条上的值的过程,变成求每个横条的过程

观察n a b c的位置,令m=(an+b)/c,所以就可以从f(a,b,c,n)递归到f(c,c-b-1,a,m-1)

直接记公式就好,a>=c或b>=c时,

其等价意义,相当于在y=(ax+b)/c下画了一条y=((a-a%c)x+(b-b%c))/c的直线,将梯形分成两个部分

而新直线是整除c的,所以蓝色内面积可以直接统计整点个数,

上面的三角形内整点个数利用坐标变换再求,斜率和截距就降到<c了,感性理解一下就好

一般常用的类欧,f、g、h

è¿éåå¾çæè¿°

对于n1e9级别的询问,f是O(logn)的,g和h大概在1e7水准

f模板(前者利用ull自然取模)

#include<cstring>
#include<cstdio>
#define ll unsigned long long

using namespace std;

// sum{i=0}^{n} floor((a*i+b)/c)
ll f(ll a,ll b,ll c,ll n){
    if(a==0){
        return (n+1)*(b/c);
    }
    if(a<c && b<c){
        ll m=(a*n+b)/c;
        if(m==0)return 0;
        return n*m-f(c,c-b-1,a,m-1);
    }
    return f(a%c,b%c,c,n)+(n+1)*(b/c)+((n+1)/(1+(n&1)))*(n/(2-(n&1)))*(a/c);
}
LL S(LL k)
{   
    return (k*(k+1)/2ll)%MOD;
}
LL f(LL a,LL b,LL c,LL n)
{
    if(!a)return (n+1)*(b/c)%MOD;
    if(a>=c || b>=c)
      return ((a/c)*S(n)%MOD+(n+1)*(b/c)%MOD+f(a%c,b%c,c,n))%MOD;
    LL m=(a*n+b)/c;
    return (m*n%MOD-f(c,c-b-1,a,m-1)+MOD)%MOD;
}

g的模板

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int mo=1e9+7,inv2=500000004,inv6=166666668;

typedef long long LL;

int a,b,c,l,r;

struct data
{
    int f,g,h;
};

data calc(int a,int b,int c,LL n)
{
    data tmp;
    if (!a)
    {
        tmp.f=tmp.g=tmp.h=0;
        return tmp;
    }
    if (a>=c || b>=c)
    {
        tmp=calc(a%c,b%c,c,n);
        n%=mo;
        tmp.h=(tmp.h+
                n*(n+1)%mo*(2*n+1)%mo*inv6%mo*(a/c)%mo*(a/c)%mo
                  +(n+1)*(b/c)%mo*(b/c)%mo
                    +(LL)2*(a/c)*tmp.g%mo
                      +(LL)2*(b/c)*tmp.f%mo
                        +n*(n+1)%mo*(a/c)%mo*(b/c))%mo;
        tmp.f=(tmp.f
                +n*(n+1)/2%mo*(a/c)
                    +(n+1)*(b/c))%mo;
        tmp.g=(tmp.g
                +n*(n+1)%mo*(2*n+1)%mo*inv6%mo*(a/c)
                    +n*(n+1)/2%mo*(b/c))%mo;
        return tmp;
    }
    LL m=((LL)a*n+b)/c;
    data nxt=calc(c,c-b-1,a,m-1);
    n%=mo; m%=mo;
    tmp.f=((n*m-nxt.f)%mo+mo)%mo;
    tmp.g=(LL)((n*(n+1)%mo*m-nxt.f-nxt.h)%mo+mo)*inv2%mo;
    tmp.h=((m*(m+1)%mo*n-(LL)2*(nxt.g+nxt.f)%mo-tmp.f)%mo+mo)%mo;
    return tmp;
}

int main()
{
    freopen("task.in","r",stdin); freopen("task.out","w",stdout);
    scanf("%d%d%d%d%d",&a,&c,&b,&l,&r);
    printf("%d\n",(calc(a,b,c,r).g-calc(a,b,c,l-1).g+mo)%mo);
    return 0;
}

例题

JZOJ3492 数数(count)

我们知道,一个等差数列可以用三个数A,B,N表示成如下形式: 
B+A,B+2A,B+3A⋯B+NA
ztxz16想知道对于一个给定的等差数列,把其中每一项用二进制表示后,一共有多少位是1
A<=1e4,B<=1e16,N<=1e12

有个很经典的类欧套路,k从0开始

二进制下,第k位是否为1,等于(原数>>k)-2*(原数>>(k+1))

前者取到了自第k位起的高位,后者高位对齐减掉了(k+1)位以上的高位

可以把i从1到n变成i从0到n-1,也就是提一个A出来,再做,于是就是类欧板子题

#include<cstring>
#include<cstdio>
#define ll unsigned long long

using namespace std;

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

int main(){
    int t;scanf("%d",&t);
    for(int i=1;i<=t;i++){
        ll a,b,n,ans=0;scanf("%llu %llu %llu",&a,&b,&n);
        for(ll lo=1;lo<=b+a*n;lo=lo+lo)ans+=f(a,b+a,lo,n-1)-f(a,b+a,lo+lo,n-1)*2;
        printf("%llu\n",ans);
    }
}

【GDOI2018模拟8.8】超级绵羊异或

求(a) xor (a + b) xor (a + b * 2) xor … xor (a + b * (n - 1))。

对于100%的数据,t<=1e4,a, n<=1e9, b<=1e9;

写成求和形式,考虑最后第k位的奇偶,即像上题一样,判断每一位的出现次数,再类欧

但因为只关注第k位的奇偶,所以也可以模一下直接算一次f函数,

CCPC2017杭州的L题类欧(hdu6275)就被算两次的f卡了常,此时,只需编写对2取模的f函数即可

或者保持原f函数不变,只对答案取模2,二者是等价的,

#include<cstring>
#include<cstdio>
#define ll unsigned long long

using namespace std;

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

int main(){
    freopen("shxor.in","r",stdin);
    freopen("shxor.out","w",stdout);
    int t;scanf("%d",&t);
    for(int i=1;i<=t;i++){
        ll a,b,n,ans=0;scanf("%llu %llu %llu",&n,&a,&b);
        for(ll lo=1;lo<=(n-1)*b+a;lo=lo+lo){
            ll tmp=f(b,a,lo,n-1)-f(b,a,lo+lo,n-1)*2;
            ans+=(tmp&1)*lo;
        }printf("%llu\n",ans);
    }
    fclose(stdin);fclose(stdout);
    return 0;
}

BZOJ3817

对于 100% 的数据,满足 n≤10^9,r≤10^4,T≤10^4。

考虑幂的奇偶会对答案造成不同贡献,本质上是幂的第1位是否为1

仍利用本式求Ans0,注意[bool]取值只有0和1,但本题中若为0则应+1,若为1则应-1

所以对布尔式的贡献线性变换一下,搞成1-2*Ans0

后续,由于sqrt(r)是实数,实数类欧不会搞,

不想学了,留坑,无限期待填

CSPS初赛知识点整理主要涵盖数学、计算机科学和信息技术等多个领域。其中,数学是CSPS竞赛的重要组成部分,涉及的知识点包括数论、代数、几何、概率与统计等。在数论方面,需要掌握素数、质因数分解、欧几里得算法、同余和模运算等相关概念和定理。在代数方面,需要了解多项式运算、方程与不等式、函数与图像等内容。在几何方面,需要熟悉平面几何的基本理论、线性代数的知识以及空间几何的相关概念。在概率与统计方面,需要了解随机事件、概率计算、统计推断、数据分析等内容。 此外,CSPS初赛还需要掌握计算机科学和信息技术方面的知识。在计算机科学方面,需要了解算法与数据结构、计算机组成原理、操作系统、编程思想等内容。在算法与数据结构方面,需要熟悉常见的数据结构如数组、链表、栈、队列、树、图等,并能应用各种算法进行问题的解决。在计算机组成原理方面,需要了解计算机的基本组成部分,包括CPU、存储器、输入输出设备等,并能了解各个组件的工作原理。在操作系统方面,需要了解操作系统的基本概念和功能,如进程管理、内存管理、文件系统等。在编程思想方面,需要掌握面向对象编程、函数式编程、并发编程等基本思想。 在信息技术方面,需要了解网络与通信、数据库管理系统等相关知识。在网络与通信方面,需要了解互联网的基本组成和架构、网络协议、网络安全等内容。在数据库管理系统方面,需要了解数据库基本概念、关系模型、SQL语言和常见的数据库管理系统等知识。 综上所述,CSPS初赛知识点整理包括数学、计算机科学和信息技术等多个领域的知识。掌握这些知识点将有助于参赛者在竞赛中取得优异的成绩。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值