刚开始看的时候以为是线段树,怎么想也想不通,看了思路之后,才知道条件给的很优越,用容斥原理统计区间和,由于m不大,再用辗转相除统计改变的数,注意用64位整数。
ACcode:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
using std::map;
typedef long long LL;
const int nsize=440000;
const int msize=1111;
map <int,int> c;
map <int,int>::iterator ct;
int factor[110],cnt;
int gcd(int aa,int bb)
{
return bb==0?aa:gcd(bb,aa%bb);
}
int main()
{
LL sum,res;
int a,b,k,num,s,w;
int T,n,m,op,p,q,x,y,ans;
scanf("%d",&T);
while (T--)
{
c.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) k=x,x=y,y=k;
res=0,cnt=0,q=p;
for (int j=2;j<=(int)(sqrt((double)p));j++)
{
if (p%j==0)
{
factor[cnt++]=j;
while (p%j==0) p/=j;
}
}
if (p>1) factor[cnt++]=p;
p=(1<<cnt);
for (int j=0;j<p;j++)
{
ans=w=0,k=j,s=1;
while (k>0)
{
if (k&1) ans++,s*=factor[w];
k>>=1,w++;
}
a=(x-1)/s*s+s;
b=y/s*s;
sum=(LL)(a+b)*((b-a)/s+1)/2;
if (ans&1) res-=sum;
else res+=sum;
}
for (ct=c.begin();ct!=c.end();ct++)
{
a=ct->first,b=ct->second;
if (a<x||a>y) continue;
if (gcd(a,q)==1) res-=a;
if (gcd(b,q)==1) res+=b;
}
printf("%I64d\n",res);
}
else
{
scanf("%d %d",&x,&y);
c[x]=y;
}
}
}
return 0;
}