题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4407
题意:
一个长度为n的序列,开始的序列为1,2,3....n;
然后又两种操作。
operation1: 询问区间[a,b]中与p互质的数的和。
operation2:将序号为i的数修改为x;
如果没有修改操作,那么狠明显就是一个容斥原理
计数的问题。
由于加上了修改,但是次数不多,因此我们可以将
修改后的点记录下来,然后先容斥记下数,然后遍历
记录的数组判断修改后的数是否要加上,之前的数
是否要去掉。
代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
#include <vector>
using namespace std;
typedef long long LL;
map<int,int > mp;
map<int,int >::iterator it;
vector<int >vc;
void fen(int x){
vc.clear();
for(int i=2;i*i<=x;i++){
if(x%i==0){
vc.push_back(i);
while(x%i==0) x/=i;
}
}
if(x>1) vc.push_back(x);
}
LL solve(int x,int p){
LL ans =(LL)x*(x+1)/2;
fen(p);
//cout<<"vc.size() "<<vc.size()<<endl;
for(int i=1;i<(1<<vc.size());i++){
int cnt = 0;
LL tmp = 1;
for(int j=0;j<vc.size();j++){
if((1<<j)&i) tmp*=vc[j],cnt++;
}
LL k = x/tmp;
k = (k+1)*k/2*tmp;
if(cnt%2) ans -= k;
else ans += k;
}
return ans;
}
int gcd(int a,int b){
return b ? gcd(b,a%b) : a;
}
int main()
{
int t,n,m,ord,x,y,p;
scanf("%d",&t);
while(t--){
mp.clear();
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
scanf("%d",&ord);
if(ord==2){
scanf("%d%d",&x,&y);
mp[x]=y;
}
else{
scanf("%d%d%d",&x,&y,&p);
LL ans = solve(y,p)-solve(x-1,p);
//printf("ans= %d\n",ans);
for(it=mp.begin();it!=mp.end();it++){
if(it->first>=x&&it->first<=y){
if(gcd(it->first,p)==1) ans -= it->first;
if(gcd(it->second,p)==1) ans +=it->second;
}
}
printf("%I64d\n",ans);
}
}
}
return 0;
}