原题: http://acm.hdu.edu.cn/showproblem.php?pid=4407
题意:
原来的n长数组123…,有两种操作,一种是单点修改,一种是查询区间与p互质的数之和。
解析:
因为操作只有1000个,所以对于修改的数可以暴力做,对于原始的位置相当于做 [ 1 , R ] [1,R] [1,R]中与p互质的数之和。
这个用莫比乌斯做,设 f ( d ) = ∑ i < = R i ∗ [ g c d ( i , p ) = d ] f(d)=\sum_{i<=R}i*[gcd(i,p)=d] f(d)=∑i<=Ri∗[gcd(i,p)=d], g ( d ) = ∑ i < = R i ∗ [ d ∣ g c d ( i , p ) ] g(d)=\sum_{i<=R}i*[d|gcd(i,p)] g(d)=∑i<=Ri∗[d∣gcd(i,p)],有 g ( d ) = d + 2 d + . . . k d ( k d + d > R ) g(d)=d+2d+...kd(kd+d>R) g(d)=d+2d+...kd(kd+d>R)
g ( d ) g(d) g(d)表示的是 g c d ( i , p ) = k d gcd(i,p)=kd gcd(i,p)=kd,而 f ( d ) f(d) f(d)表示的是 g c d ( i , p ) = d gcd(i,p)=d gcd(i,p)=d
注意,此时的d的取值必须是p的因子,这个条件很关键,降低了时间复杂度
因为有 g ( d ) = ∑ f ( i ∗ d ) g(d)=\sum f(i*d) g(d)=∑f(i∗d),所以 f ( d ) = ∑ u ( i ) ∗ g ( i ∗ d ) f(d)=\sum u(i)*g(i*d) f(d)=∑u(i)∗g(i∗d)。
答案为 f ( 1 ) = ∑ i u ( i ) ∗ g ( i ) f(1)=\sum_{i}u(i)*g(i) f(1)=∑iu(i)∗g(i)
这里的 i i i只需要枚举p的因子即可。
代码:
#include
using namespace std;
#define LL long long
const int maxn=4e5+5;
int mu[maxn],pri[maxn>>1],now;
bool vis[maxn];
void init(){
mu[1]=1;
for(int i=2;i<maxn;i++){
if(!vis[i]){
pri[++now]=i;
mu[i]=-1;
}
for(int j=1;j<=now&&pri[j]*i<maxn;j++){
vis[pri[j]*i]=1;
if(i%pri[j])mu[i*pri[j]]=-mu[i];
else{
mu[i*pri[j]]=0;
break;
}
}
}
}
vector<int>fac[maxn];
LL deal(int r,int p){
if(r<=0)return 0;
if(fac[p].empty()){
int en=sqrt(p);
for(int i=1;i<=en;i++){
if(p%i==0){
fac[p].push_back(i);
if(p/i!=i){
fac[p].push_back(p/i);
}
}
}
}
LL ans=0;
for(auto y:fac[p]){
LL st=(LL)y;
LL en=r/y*y;
ans+=(LL)(mu[y])*(st+en)*(en/st)/2ll;
}
return ans;
}
int main(){
init();
int t;scanf("%d",&t);
unordered_map<int,int>re;
while(t--){
int n,q;scanf("%d%d",&n,&q);
re.clear();
while(q--){
int flag;scanf("%d",&flag);
if(flag==1){
int l,r,p;scanf("%d%d%d",&l,&r,&p);
LL ans=deal(r,p)-deal(l-1,p);
for(auto y:re){
if(y.first<=r&&y.first>=l){
if(__gcd(y.first,p)==1)ans-=(LL)y.first;
if(__gcd(y.second,p)==1)ans+=(LL)y.second;
}
}
printf("%lld\n",ans);
}
else{
int a,b;scanf("%d%d",&a,&b);
re[a]=b;
}
}
}
}