参考https://blog.csdn.net/qq_15714857/article/details/47954263
题意:
给一个长度为n的序列,序列由1~n依次组成。
对序列执行两种操作:
1.查询[x,y]内与p互素的数的和;
2.修改第x个数为c.
思路:
2修改操作用map进行映射。
查询时,把序列一直当做1~n的序列来查询,然后迭代器跑一遍map判断对查询有无影响;
对于查询操作,我们可以先分解出p的质因数,设p的每种质因数组合的里的质因数乘积为value,那么[x,y]内value的倍数与p必定不互素,求出value的区间[x,y]内所有倍数和(用等比数列求和公式cal()),然后对结果进行容斥求和,即得出区间[x,y]内与p不互素的数的和sum,然后区间所有数的和(用等差数列求和公式)减去sum即可。
代码:
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<map>
#include<algorithm>
using namespace std;
#define N 2300010
#define ll long long
const int maxn=30005;
const ll mod=1000000007;
map<int,int>v;
vector<int>fac;
int gcd(int x,int y)
{
if(y==0) return x;
return gcd(y,x%y);
}
ll cal( int l , int r , int val ){//l-r区间val的倍数的和
int n = ( r / val ) - ( ( l - 1 ) / val ) ;
int a1 = ( l % val == 0 )? l : ( val - l % val ) + l ;
int an = r - r % val ;
ll res = (ll)( a1 + an ) * (ll)n / 2 ;
return res ;
}
ll work(int l,int r,int p)
{
fac.clear();
for(int i=2;i*i<=p;i++)
{
if(p%i==0)
{
fac.push_back(i);
while(p%i==0) p/=i;
}
}
if(p>1) fac.push_back(p);//获得p的质因子
int s=fac.size();
ll res=0;
for(int i=1;i<(1<<s);i++)
{
int bits=0;
ll val=1;
for(ll j=0;j<s;j++)
{
if(i&(1<<j))
{
bits++;
val*=fac[j];
}
}
ll tmp=cal(l,r,val);
if(bits&1) res+=tmp;//容斥
else res-=tmp;
}
ll sum=(ll)(l+r)*(ll)(r-l+1)/2;
res=sum-res;
return res;
}
ll solve(int l,int r,int p)
{
ll res=work(l,r,p);
if(v.empty()) return res;
map<int,int>::iterator it;
for(it=v.begin();it!=v.end();it++)
{
ll x=it->first,y=it->second;
if(x>r||x<l) continue;
if(gcd(x,p)==1)res-=x;
if(gcd(y,p)==1) res+=y;
}
return res;
}
int main(){
int op,n,m,t,x,y,p;
scanf("%d",&t);
while(t--)
{
v.clear();
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d%d%d",&x,&y,&p);
if(x>y) swap(x,y);
ll ans=solve(x,y,p);
printf("%lld\n",ans);
}
else
{
scanf("%d%d",&x,&y);
v[x]=y;
}
}
}
return 0;
}