题目描述
有三个下标从1到n的数组a、b、c。
a数组初始全为0。
b[i]=∑j|ia[j],c[i]=∑j|ib[j]
需要进行下列操作:
1 x y :将a[x]加上y
2 x :询问当前c[x]的值
数据范围(1<=n,q<=1,000,000,x随机,1<=x<=n,1<=y<=10^6)
考虑用a表示c
c[i]=∑j|ib[j]=∑j|i∑j′|ja[j′] 交换一下主体可得
c[i]=∑j′|ia[j′]∗∑nj′d=1[d∗j′|i] 因为j’|i,d*j’|i,所以 d|nj′
设f[i]表示i的约数个数,
则有 c[i]=∑j′|ia[j′]∗f[nj′]
f可以线筛,而然后呢?c要怎么算啊???
可以想到很多n√n的算法(比如定期重构)然而,并不能过。
注意条件:x随机
也就是说,暴力的期望复杂度为O(n log n)
还有,读入优化要很fast才行。
然后。。。就没有然后了。。。
代码
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cctype>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define ll long long
using namespace std;
const int maxn=1000000+5;
const int BufferSize=1<<16;
char buffer[BufferSize],*head,*tail;
int i,j,n,c[maxn/3],q,f[maxn],g[maxn];
ll a[maxn];
bool bz[maxn];
inline char Getchar() {
if(head==tail) {
int l=fread(buffer,1,BufferSize,stdin);
tail=(head=buffer)+l;
}
return *head++;
}
inline int read() {
int x=0;char c=Getchar();
for(;!isdigit(c);c=Getchar());
for(;isdigit(c);c=Getchar()) x=x*10+c-'0';
return x;
}
int main(){
n=read(),q=read();
f[1]=1;
fo(i,2,n){
if (!bz[i]) c[++c[0]]=i,f[i]=2,g[i]=1;
fo(j,1,c[0]){
if (i>n/c[j]) break;
bz[i*c[j]]=1;
if (i%c[j]==0) {
g[i*c[j]]=g[i]+1;
f[i*c[j]]=f[i]/(g[i]+1)*(g[i*c[j]]+1);
break;
}
f[i*c[j]]=f[i]*2;g[i*c[j]]=1;
}
}
fo(i,1,q){
int o,x,y;
o=read(),x=read();
if (o==1) {
y=read();
fo(j,1,n/x) a[x*j]=a[x*j]+(ll)y*f[j];
}else printf("%lld\n",a[x]);
}
}