![[bzoj3813]奇数园 [bzoj3813]奇数园](https://image.shishitao.com:8440/aHR0cHM6Ly9ia3FzaW1nLmlrYWZhbi5jb20vdXBsb2FkL2NoYXRncHQtcy5wbmc%2FIQ%3D%3D.png?!?w=700&webp=1)
仿佛现在已经完成了做题之前先开个坑的习惯,也许是为了逼迫自己去刷一些神题吧。。。然并卵,该剩的好多坑还是剩着呢。
【bzoj3813】一道线段树好题。已经把数论忘光光了。
欧几里德算法
扩展欧几里德算法概述
gcd函数就是用来求(a,b)的最大公约数的。
gcd函数的基本性质:
gcd(a,b)=gcd(b,a)=gcd(-a,b)=gcd(|a|,|b|)
扩展欧几里德算法公式表述
gcd(a,b)=gcd(b,a mod b)
证明:a可以表示成a = kb + r,则r = a mod b
假设d是a,b的一个公约数,则有
d|a, d|b,而r = a - kb,因此d|r
因此d是(b,a mod b)的公约数
假设d 是(b,a mod b)的公约数,则
d | b , d |r ,但是a = kb +r
因此d也是(a,b)的公约数
因此(a,b)和(b,a mod b)的公约数是一样的,其最大公约数也必然相等,得证
扩展算法
对于不完全为 0 的非负整数 a,b,gcd(a,b)表示 a,b 的最大公约数,必然存在无数组整
数对 x,y ,使得 gcd(a,b)=ax+by。
1,显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;
2,a>b>0 时
设 ax1+ by1= gcd(a,b);
bx2+ (a mod b)y2= gcd(b,a mod b);
根据朴素的欧几里德原理有 gcd(a,b) = gcd(b,a mod b);
则:ax1+ by1= bx2+ (a mod b)y2;
即:ax1+ by1= bx2+ (a - [a / b] * b)y2=ay2+ bx2- [a / b] * by2;
也就是ax1+ by1 == ay2+ b(x2- [a / b] *y2);
根据恒等定理得:x1=y2; y1=x2- [a / b] *y2;
这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2.
上面的思想是以递归定义的,因为 gcd 不断的递归求解一定会有个时候 b=0,所以递归可以结束。
贴了上面这么多内容,无非是想说明,若能找到ax+by==1,则(a,b)=1,即题中求出φ(i);
简述一下题目,询问一段区间内的累乘,求它的欧拉函数。
欧拉函数就是:φ(n) = n * (1 - 1 / p1) * (1 - 1 / p2) * (1 - 1 / p3) * (1 - 1 / p4)……*(1 - 1 / pn)
= n / (p1 * p2 * p3 * …… * pn) * ((p1 - 1) * (p2 - 1) * (p3 - 1) * …… * (pn - 1))
这道题也是uoj的#38,uoj的blog上面有比较详细的解法,我看的陆爷的blog感觉写得蛮优美..
关于φ的求法有很多种,这里数字*π(pri[i]-1)/pri[i]即答案。只要60位记录一下状态即可。
呵呵哒,又get到一种逆元的新求法,不过没关系啦,考试的时候忘记了大不了写个quickmi
#include<cstdio> #include<algorithm> #define ll long long #define mo 19961993 #define N 400050 using namespace std; ,n,tot=; ll ni[],pri[]; struct node{ int l,r; ll v; }tree[][]; void calc(int f,int k,int val) { )tree[f][k].v=val; else { tree[f][k].v=; ;i<=;i++) )tree[f][k].v+=1ll<<(i-); } } void update(int f,int p) { )tree[f][p].v=tree[f][p+p].v*tree[f][p+p+].v%mo; ].v; } void build(int f,int p,int l,int r) { tree[f][p].l=l;tree[f][p].r=r;; if(l==r){ calc(f,p,);return; } build(f,p+p,l,mid); build(f,p+p+,mid+,r); update(f,p); } ll que(int f,int p,int x,int y) { ; if(x==l&&r==y)return tree[f][p].v; if(y<=mid)return que(f,p+p,x,y); ,x,y); else{ ),mid+,y)%mo; ,mid+,y); } } ll query(int xx,int yy) { ll tmp1=que(,,xx,yy),tmp2=que(,,xx,yy); ;i<=;i++) ))) tmp1 = tmp1*(pri[i]-) % mo * ni[pri[i]] %mo; return tmp1; } void change(int p,int x,int y) { ][p].l==x&&tree[][p].r==x){ tree[][p].v=y;tree[][p].v=; ;i<=;i++) )tree[][p].v+=1ll<<(i-); return; } ][p].l+tree[][p].r)/; ,x,y); tree[][p].v=tree[][p+p].v*tree[][p+p+].v%mo; tree[][p].v=tree[][p+p].v|tree[][p+p+].v; } int main() { ni[]=; int flag[N]; ;i<M;i++) { ni[i] = -mo/i * ni[mo%i] % mo; ni[i]=(ni[i]+mo)%mo; ){tot++;pri[tot]=i;} ;j<=tot;j++) { if(i*pri[j]>M)break; flag[i*pri[j]]=; )break; } } build(,,,); build(,,,); scanf("%d",&n); ;i<=n;i++) { int op,x,y; scanf("%d%d%d",&op,&x,&y); )printf("%lld\n",query(x,y)); ,x,y); } }
bzoj3813
这道题下午吃完午饭来写,困到最后只剩手在动也没啥知觉了。。。不写挂真是谢天谢地!