题目:
操作次数很少(<=1000),所以可以先求出[1,n]中与p互质的数之和为多少,然后根据所有的修改来调整答案
求出[1,n]中与p互质的数之和,可以用莫比乌斯反演,复杂度O(2^t) 其中t 为p的不同的质因数个数。
然后调整答案就好。
莫比乌斯反演是这么做的:
然后,F(n,1)就是答案,让u(d)不为0的数只有 2^t 个,其中t 为p的不同的质因数个数。
求出p的所有不同的质因数,然后每个质因数依次取0和1次方,一共2^t个值,都计算就行了。
代码如下:
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #define inf 0x5fffffff #define FOR(i,n) for(long long (i)=1;(i)<=(n);(i)++) #define out(i) <<#i<<"="<<(i)<<" " #define OUT1(a1) cout out(a1) <<endl #define OUT2(a1,a2) cout out(a1) out(a2) <<endl #define OUT3(a1,a2,a3) cout out(a1) out(a2) out(a3)<<endl #define maxn 400007 #define maxnn 1200000 typedef long long LL; using namespace std; LL gcd(LL a,LL b){while(b){int t=a%b;a=b;b=t;} return a;} //Number Theory int maxp[maxn]; int u[maxn]; void Init(){ u[0]=0; for(int i=1;i<=400000;++i) u[i]=1,maxp[i]=-1; for(int i=2;i<=400000;++i){ if(~maxp[i]) continue; for(int j=1;j<=400000/i;++j){ maxp[j*i]=i; if(j%i==0) u[j*i]=0; else u[j*i]=-u[j*i]; } } } struct Change{ int x,Nx; }C[1007]; int XP[50],Xn,Cn; void F(int x){ Xn=0; while(x>1){ XP[Xn]=maxp[x]; while(x%XP[Xn]==0) x/=XP[Xn]; Xn++; } } LL G(LL k,LL n){return k*(n/k)*(n/k+1)/2;} LL Cal(int x,LL n){//计算[0,n] if(!n) return 0; F(x);LL ANS=0; for(int i=0;i<(1<<Xn);++i){ int I=1,X=i; for(int j=0;j<Xn;++j,X>>=1){ if(X&1) I*=XP[j]; } ANS+=u[I]*G(I,n); } return ANS; } int n,m; int main() { Init(); int op,x,y,p,T; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m);Cn=0; for(int i=0;i<m;++i){ scanf("%d%d%d",&op,&x,&y); if(op==1){ scanf("%d",&p); //Query x y p LL ANS=Cal(p,y)-Cal(p,x-1);//Caculate for(int j=0;j<Cn;++j){//Adjust Value if(C[j].x>=x&&C[j].x<=y){ if(gcd(C[j].x,p)==1) ANS-=C[j].x; if(gcd(C[j].Nx,p)==1) ANS+=C[j].Nx; } } printf("%I64d\n",ANS); } else{ //Change x to y bool T=false; for(int i=0;i<Cn;++i){ if(C[i].x==x){ C[i].Nx=y; T=true; break; } } if(!T){ C[Cn].x=x; C[Cn].Nx=y; Cn++; } } } } return 0; }