题意:
对于一个序列原来是1~n,现在又两个操作,操作1、给定区间[l,r]求这个区间中与p互质的数的个数;操作2、将区间的第i个数变成a。
题解:
如果没有替换的操作,对于求某个区间与p互质的个数,可以分别求[1,l]和[1,r]的,然后相减。对于求某个区间的数是否与p互质,明显不好求,那么我们可以求不互质的个数,不互质的个数就是p的质因子倍数的个数,这个显然要用容斥来计算,在计算时我们要把个数转化成和,dfs一下即可,计算和时用到了等差数列的前n项和。
那么没有替换操作的分析完了,那么加上替换操作该如何解决?看了下操作数,1000,恩,这个数字还算可以接受,那么我们这样做:将替换的操作记录下来,然后先计算没替换时对应的和,然后根据替换的操作进行后期处理,如果替换操作出现在当前区间,那么就单独判断下这个替换后的数,对结果进行调整。这个调整很easy的,对于操作的存储就用map就好了。
#include<iostream> #include<math.h> #include<stdio.h> #include<algorithm> #include<string.h> #include<vector> #include<queue> #include<map> #include<set> using namespace std; #define B(x) (1<<(x)) void cmax(int& a,int b){ if(b>a)a=b; } void cmin(int& a,int b){ if(b<a)a=b; } typedef long long ll; const int oo=0x3f3f3f3f; const ll OO=1LL<<61; const int MOD=10007; const int maxn=400005; const double eps=1e-8; int p[maxn][15],f[maxn]; map<int,int>mat; void Prime(){ for(int i=2;i<maxn;i++){ if(f[i])continue; p[i][++p[i][0]]=i; for(int j=i*2;j<maxn;j+=i){ f[j]=1; p[j][++p[j][0]]=i; } } } int gcd(int a,int b){ return b ? gcd(b,a%b) : a; } ll q_sum(ll d,int n){ return d*n+d*n*(n-1)/2; } ll dfs(int s,ll x,int n,int num){ ll res=0,t; for(int i=s;i<=p[num][0];i++){ t=x*p[num][i]; res+=q_sum(t,n/t)-dfs(i+1,t,n,num); } return res; } ll get_sum(int n,int num){ if(n<=0)return 0; ll sum=(ll)n*(n+1)/2; return sum-dfs(1,1,n,num); } int main(){ //freopen("E:\\read.txt","r",stdin); int T,n,m,op,a,b,num,t; ll ans; map<int,int>::iterator it; Prime(); scanf("%d",&T); while(T--){ scanf("%d %d",&n,&m); mat.clear(); while(m--){ scanf("%d",&op); if(op==1){ scanf("%d %d %d",&a,&b,&num); ans=get_sum(b,num)-get_sum(a-1,num); for(it=mat.begin();it!=mat.end();++it){ if(a<=it->first&&it->first<=b){ t=it->first; if(gcd(t,num)==1)ans-=t; t=it->second; if(gcd(t,num)==1)ans+=t; } } printf("%I64d\n",ans); }else{ scanf("%d %d",&a,&num); mat[a]=num; } } } return 0; } /** 1 130 5 1 1 130 26 2 13 58 1 1 130 39 1 39 130 130 1 41 141 1 */