HDU 4407 Sum (莫比乌斯反演)

题目:

操作次数很少(<=1000),所以可以先求出[1,n]中与p互质的数之和为多少,然后根据所有的修改来调整答案

求出[1,n]中与p互质的数之和,可以用莫比乌斯反演,复杂度O(2^t) 其中t 为p的不同的质因数个数。

然后调整答案就好。

莫比乌斯反演是这么做的:

HDU 4407 Sum (莫比乌斯反演)_第1张图片

HDU 4407 Sum (莫比乌斯反演)_第2张图片

然后,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;
}




你可能感兴趣的:(HDU 4407 Sum (莫比乌斯反演))