bzoj3813(线段树+乘法逆元+欧拉函数)
2016-08-24 14:42
176 查看
给定一个序列,每个数都由60个最小的素数的乘积构成,
求某段的乘积的欧拉函数值对19961993取模后的值,支持单点修改
这题看似有难度,实际上主要是题目描述太恶心了,oi真是综合,把语文连着一块考。。
关键还是分析出一个数的phi构造,
n=π pi^ki
那么他的phi就是:(pi-1)*pi^(ki-1)
其实通俗点就是,对于每一个质因子k,n / k *(k-1),我们知道这道题当中的质因子只有可能有60个,我用bitset的线段树来维护一个区间出现了哪些质因子,另一个线段树维护,区间之积。得到这两个之后,就根据之前的出现的质因子来计算积的phi就好
因为要除法所以需要乘法逆元,因为质因子只有60个,所以需要的逆元也只需要这个60个(实际上phi也只需要这60个),
又因为需要提区间并且单点并且乘积这个信息可以合并更新,所以需要线段树
关键还是这种计算phi的方式比较重要
总结:对题目描述的理解非常的重要,这道题许多的转弯,实际上完全可以用一句话来解释。所以锻炼读题的能力也是很需要的,注意题目中的细节,也许就是问题模型的一个约束
求某段的乘积的欧拉函数值对19961993取模后的值,支持单点修改
这题看似有难度,实际上主要是题目描述太恶心了,oi真是综合,把语文连着一块考。。
关键还是分析出一个数的phi构造,
n=π pi^ki
那么他的phi就是:(pi-1)*pi^(ki-1)
其实通俗点就是,对于每一个质因子k,n / k *(k-1),我们知道这道题当中的质因子只有可能有60个,我用bitset的线段树来维护一个区间出现了哪些质因子,另一个线段树维护,区间之积。得到这两个之后,就根据之前的出现的质因子来计算积的phi就好
因为要除法所以需要乘法逆元,因为质因子只有60个,所以需要的逆元也只需要这个60个(实际上phi也只需要这60个),
又因为需要提区间并且单点并且乘积这个信息可以合并更新,所以需要线段树
关键还是这种计算phi的方式比较重要
#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #include<bitset> using namespace std; typedef long long ll; const ll mod=19961993; const int n=200200; int id[500]; ll ny[100]; int p[100],phi[500]; bool b[300]; struct aa { int l,r; ll mul; bitset<70> cnt; }a[4*n]; void up(int i) { a[i].mul=1; if (a[i<<1].l) a[i].mul=a[i<<1].mul*a[i].mul%mod; if (a[i<<1|1].l) a[i].mul=a[i<<1|1].mul*a[i].mul%mod; a[i].cnt.reset(); a[i].cnt=a[i<<1].cnt|a[i<<1|1].cnt; } void build(int i,int l,int r) { a[i].l=l;a[i].r=r; if (l==r) { a[i].mul=3; a[i].cnt.set(2); return ; } int mid=(l+r)>>1; build(i<<1,l,mid); build(i<<1|1,mid+1,r); up(i); } ll query_mul(int i,int l,int r) { if (a[i].l==l&&a[i].r==r) return a[i].mul; int mid=(a[i].l+a[i].r)>>1; if (mid>=r) return query_mul(i<<1,l,r); else if (mid<l) return query_mul(i<<1|1,l,r); return query_mul(i<<1,l,mid)*query_mul(i<<1|1,mid+1,r)%mod; } bitset<70> query_bit(int i,int l,int r) { if (a[i].l==l&&a[i].r==r) return a[i].cnt; int mid=(a[i].l+a[i].r)>>1; if (mid>=r) return query_bit(i<<1,l,r); else if (mid<l) return query_bit(i<<1|1,l,r); return query_bit(i<<1,l,mid)|query_bit(i<<1|1,mid+1,r); } void work(int l,int r) { ll mull=query_mul(1,l,r); bitset<70> cnt=query_bit(1,l,r); for (int i=1;i<=60;i++) if (cnt.test(i)) { mull=mull*ny[i]%mod; mull=mull*(p[i]-1)%mod; } printf("%lld\n",mull); } void updata(int i,int pos,int x) { if (a[i].l==a[i].r) { a[i].cnt.reset(); for (int j=1;j<=60;j++) if (x%p[j]==0) a[i].cnt.set(j); a[i].mul=x; return ; } int mid=(a[i].l+a[i].r)>>1; if (pos<=mid) updata(i<<1,pos,x); else updata(i<<1|1,pos,x); up(i); } void exgcd(ll a,ll b,ll &d,ll &x,ll &y) { if (b==0) {d=a;x=1;y=0;return ;} exgcd(b,a%b,d,y,x);y-=x*(a/b); } ll kk(ll a,ll n) { ll x,y,d; exgcd(a,n,d,x,y); return ((x%n)+n)%n; } void init() { phi[1]=1; for (int i=2;i<=281;i++) { if (!b[i]) { p[++p[0]]=i; id[i]=p[0]; phi[i]=i-1; } for (int j=1;j<=p[0]&&p[j]*i<=281;j++) { b[i*p[j]]=true; if (i%p[j]==0) { phi[i*p[j]]=phi[i]*p[j]; break; } else phi[i*p[j]]=phi[i]*(p[j]-1); } } for (int i=1;i<=p[0];i++) ny[i]=kk(p[i],mod); }// ok int main() { int x; scanf("%d",&x); init(); build(1,1,n); while (x--) { int op,x,y; scanf("%d%d%d",&op,&x,&y); if (op==0) work(x,y); else updata(1,x,y); } return 0; }
总结:对题目描述的理解非常的重要,这道题许多的转弯,实际上完全可以用一句话来解释。所以锻炼读题的能力也是很需要的,注意题目中的细节,也许就是问题模型的一个约束
相关文章推荐
- sgu 102模拟欧拉函数
- poj-Relatives(欧拉函数)
- 欧拉函数线性筛法详解
- hdu 3501 Calculation 2 (欧拉函数)
- UVa 10820 Send a Table (Farey数列&欧拉函数求和)
- 欧拉函数
- UVA 11440 - Help Tomisu(欧拉函数)
- poj 2478/3090 欧拉函数应用(Farey 序列/斜率判重)
- POJ 2478 Farey Sequence(欧拉函数)
- 欧拉函数
- SPOJ 5971 LCM Sum 欧拉函数 (或 莫比乌斯反演?)
- 【HDU2865】构造矩阵+Burnside定理+欧拉函数类似poj2888
- 欧拉函数模板
- 【欧拉函数】 HDOJ 4483 Lattice triangle
- 欧拉函数模板及例题整理
- UVa 10820 - Send a Table (求1-n中所有数的欧拉函数值)
- POJ1284_Primitive Roots【欧拉函数】
- 【数论】【筛法求素数】【欧拉函数】bzoj2818 Gcd
- 欧拉函数
- 欧拉函数