这个题目要求既对序列的某段区间求和p互质数目,又支持对某个数进行修改
一开始看到这个题,有点懵,就是因为第二个操作,如果只是第一个操作,并且是原始序列1-n,那就很简单了,按照之前写过的某个题目,求出1-y中的数目,减去1-x中的数目即可
就是第二个操作把序列打乱了,这样就不好搞了
后来还是大神博客的一句话,突然解决了问题,说:总的操作数只有1000,直接暴力搞定第二个操作就行啊
我想想,对啊,一开始只是按普通操作求出数目,然后遍历一下修改(都最多只有1000个),把修改对应投射到结果上,就行了啊。。。于是,迎仍而解。。。
怎么没想到可以暴力呢,太把第二个操作当回事了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define LL __int64
using namespace std;
const int N = 400010;
int change[N],n,m;
int calc[1010],cnt;
int prime[20],ret;
int gcd(int a,int b)
{
if (a<b) swap(a,b);
while (b)
{
int tmp=b;
b=a%b;
a=tmp;
}
return a;
}
void proc(int x)
{
ret=0;
for (int i=2;i*i<=x;i++){
if (x%i==0){
prime[ret++]=i;
while (x%i==0) x/=i;
}
}
if (x>1) prime[ret++]=x;
}
LL dfs(int d,int co,int sign,int x)
{
if (d>=ret) return 0;
LL sum=0;
if (d>=0){
LL res=x/co;
if (res>0)
sum=(LL)res*co+res*(res-1)/2*co;
sum*=(LL)sign;
}
for (int i=d+1;i<ret;i++){
sum+=dfs(i,co*prime[i],-sign,x);
}
return sum;
}
int main()
{
int t,x,y,p,op;
scanf("%d",&t);
while (t--)
{
scanf("%d%d",&n,&m);
//memset(change,0,sizeof(change[0]*(n+2)));
cnt=0;
while (m--)
{
scanf("%d",&op);
if (op==2){
scanf("%d%d",&x,&y);
change[x]=y;
bool flag=0;
for (int i=0;i<cnt;i++){
if (calc[i]==x) {flag=1;break;}
}
if (!flag) calc[cnt++]=x;
}
else{
scanf("%d%d%d",&x,&y,&p);
if (x>y) swap(x,y);
proc(p);
LL sum1=dfs(-1,1,-1,x-1);
LL sum2=dfs(-1,1,-1,y);
//cout<<sum1<<" "<<sum2<<endl;
sum2-=sum1;
LL all=(LL)(x+y)*(y-x+1)/2;
all-=sum2;
for (int i=0;i<cnt;i++){
if (calc[i]>=x && calc[i]<=y){
if (gcd(calc[i],p)==1) all-=(LL)calc[i];
if (gcd(change[calc[i]],p)==1) all+=(LL)change[calc[i]];
}
}
printf("%I64d\n",all);
}
}
}
}