51nod 1586 约数和

来源:互联网 发布:开源自动收录网站源码 编辑:程序博客网 时间:2024/05/22 06:44

题目描述

有三个下标从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|ij|ja[j]交换一下主体可得

c[i]=j|ia[j]njd=1[dj|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 longusing 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]);    }}
0 0