题意:
长度为n的数组,初始值为0。
有两种操作:
①给定a,b,x,将所有下标 i 满足 gcd(a,i)==b的位置加上x
②给定x,查询 1~x 的前缀和
思路:
对于操作①,显然无法更新所有的满足要求的下标
稍作分析可以发现,对于每次更新操作的贡献,只需要更新每个反演式的第一个位置
那么查询的时候,每个位置的贡献就是( x/i )
因为有更新操作,用树状数组来维护
复杂度不超过为O(q*sqrt(n))
代码:
#include<bits/stdc++.h>
#define lowbit(x) (x&(-x))
const int N = 200005;
using namespace std;
int n;
int mu[N],sum[N];
long long A[N];
bool Np[N];
vector<int>E[N];
vector<int>pr;
void add(int pos,int val){
for(int i=pos;i<=n;i+=lowbit(i))A[i]+=val;
}
inline long long query(int t){
long long res = 0;
while(t){
res += A[t];
t -= lowbit(t);
}return res;
}
void init(){
mu[1] = sum[1] = 1;
for(int i=2;i<N;i++){
if(!Np[i]){
pr.emplace_back(i);
mu[i] = -1;
}for(int j=0,k=pr[j]*i;k<N;k=pr[++j]*i){
Np[k] = true;
if(i%pr[j]==0){
mu[k] = 0;
break;
}mu[k] = -mu[i];
}sum[i] = sum[i-1] + mu[i];
}
for(int i=1;i<N;i++){
for(int j=i;j<N;j+=i){
E[j].emplace_back(i);
}
}
}
inline int read()
{
int x=0,f=1; char ch=getchar();
while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
long long oper(int x){
long long res = 0;
for(int i=1,last;i<=x;i=last+1){
last = x / (x/i);
res += 1LL * (x/i) * (query(last)-query(i-1));
}return res ;
}
int main()
{
init();
int q,k,a,b,x,Cas=0;
while(1){
n = read();q = read();
if(!n)break;
for(int i=0;i<=n;i++)A[i]=0;
printf("Case #%d:\n",++Cas);
while(q--){
k=read();
if(k==1){
a=read();b=read();x=read();
if(a%b)continue;a/=b;
for(int i=0;i<E[a].size();i++){
int t = E[a][i] ;
if(mu[t])add(t*b,mu[t]*x);
}
}else {
x=read();
printf("%I64d\n",oper(x));
}
}
}return 0;
}