洛谷P3312 - [SDOI2014]数表
Solution
共\(T(T\leq2\times10^4)\)组测试数据。给出\(n,m(n,m\leq10^5),a(a\leq10^9)\),求$$ \sum_{i=1}n\sum_{j=1}m [\sigma_1(gcd(i,j))\leq a]\sigma_1(gcd(i,j)) $$
Solution
\[\begin{align*}
ans &= \sum_{i=1}^n\sum_{j=1}^m [\sigma_1(gcd(i,j))\leq a]\sigma_1(gcd(i,j)) \\
&= \sum_{d=1}^{+∞} [\sigma_1(d)\leq a]\sigma_1(d) \sum_{i=1}^n\sum_{j=1}^m [gcd(i,j)=d] \\
&= \sum_{d=1}^{+∞} [\sigma_1(d)\leq a]\sigma_1(d) \sum_{d_1=1}^{+∞} \mu(d_1)⌊\frac{n}{dd_1}⌋⌊\frac{m}{dd_1}⌋ \\
&= \sum_{d=1}^{+∞} ⌊\frac{n}{d}⌋⌊\frac{m}{d}⌋ \sum_{d_1|d} [\sigma_1(d_1)\leq a]\sigma_1(d_1)\mu(\frac{d}{d_1})
\end{align*}$$ 将测试数据按$a$由小到大排序,维护$f(x)=\sum_{d|x} [\sigma_1(d)\leq a]\sigma_1(d)\mu(\dfrac{x}{d})$的前缀和。当$a$由$a_1$增大至$a_2$时,对于$d$满足$\sigma_1(d)\in(a_1,a_2]$,将$\sigma_1(d)\mu(k)$加到$f(kd)$中。预处理出$\sigma_1(x),\mu(x)$,用树状数组维护$f(x)$的前缀和即可。
> 时间复杂度$O(n+T\sqrt n+nlogn)$。
##Code
```cpp
//[SDOI2014]数表
#include <algorithm>
#include <cstdio>
using namespace std;
inline char gc()
{
static char now[1<<16],*s,*t;
if(s==t) {t=(s=now)+fread(now,1,1<<16,stdin); if(s==t) return EOF;}
return *s++;
}
inline int read()
{
int x=0; char ch=gc();
while(ch<'0'||'9'<ch) ch=gc();
while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc();
return x;
}
const int N=1e5+10;
const int N1=1e5;
int pCnt,pr[N],p1[N]; bool pNot[N];
int mu[N]; struct rec{int v,id;} f[N];
bool cmpV(rec x,rec y) {return x.v<y.v;}
void init(int n)
{
mu[1]=1,f[1].v=1;
for(int i=1;i<=n;i++) f[i].id=i;
for(int i=2;i<=n;i++)
{
if(!pNot[i])
{
pr[++pCnt]=i; p1[i]=i;
mu[i]=-1,f[i].v=i+1;
for(long long j=1LL*i*i;j<=n;j*=i) f[j].v=f[j/i].v*i+1;
}
for(int j=1;j<=pCnt;j++)
{
int x=i*pr[j]; if(x>n) break;
pNot[x]=true;
if(i%pr[j]) p1[x]=pr[j],mu[x]=-mu[i],f[x].v=f[i].v*(pr[j]+1);
else {p1[x]=p1[i]*pr[j],f[x].v=f[x/p1[x]].v*f[p1[x]].v; break;}
}
}
sort(f+1,f+n+1,cmpV);
}
struct query{int n,m,a,id;} q[20010];
bool cmpA(query x,query y) {return x.a<y.a;}
int tr[N];
void add(int x,int v) {while(x<=N1) tr[x]+=v,x+=x&(-x);}
int sum(int x) {int r=0; while(x) r+=tr[x],x-=x&(-x); return r;}
int ans[20010];
int main()
{
int Q=read(); init(N1);
for(int i=1;i<=Q;i++) q[i].n=read(),q[i].m=read(),q[i].a=read(),q[i].id=i;
sort(q+1,q+Q+1,cmpA);
for(int i=1,j=1;i<=Q;i++)
{
int n=q[i].n,m=q[i].m,a=q[i].a;
while(j<=1e5&&f[j].v<=a)
{
int d1=f[j].id;
for(int k=1;k*d1<=1e5;k++) add(k*d1,f[j].v*mu[k]);
j++;
}
if(n>m) swap(n,m);
int res=0;
for(int L=1,R;L<=n;L=R+1)
{
int v1=n/L,v2=m/L; R=min(n/v1,m/v2);
res+=v1*v2*(sum(R)-sum(L-1));
}
ans[q[i].id]=res&2147483647;
}
for(int i=1;i<=Q;i++) printf("%d\n",ans[i]);
return 0;
}
```
##P.S.
答案对$2147483647$取模,这就相当于自然溢出然后`&2147483647`。\]