P3312 [SDOI2014]数表
[SDOI2014]数表
题意即求:
\[\sum _ { i = 1 } ^ { n } \sum _ { j = 1 } ^ { m } \left[ \sigma _ 1 ( \gcd ( i , j )) \le a \right] \sigma _ 1 ( \gcd ( i , j ) ) \pmod { 2 ^ {31}}
\]
其中 \(T\le 2 \times 10 ^4\),\(n , m \le 10 ^ 5\)。
直接推导:
\[\begin{aligned}
&
\sum _ { i = 1 } ^ { n } \sum _ { j = 1 } ^ { m } \left[ \sigma _ 1 ( \gcd ( i , j )) \le a\right] \sigma _ 1 ( \gcd ( i , j ) )
\\
= &
\sum _ { d = 1 } \sum _ { i = 1 } ^ { n } \sum _ { j = 1 } ^ { m } \left[ \sigma _ 1 ( d ) \le a \right] \left[ \gcd ( i , j ) = d \right] \sigma _ 1 ( d )
\\
= &
\sum _ { d = 1 } \left[ \sigma _ 1 ( d ) \le a \right] \sigma _ 1 ( d ) \sum _ { i = 1 } ^ { \left\lfloor n / d \right\rfloor } \sum _ { j = 1 } ^ { \left\lfloor m / d \right\rfloor } \left[ \gcd ( i , j ) = 1 \right]
\\
= &
\sum _ { d = 1 } \left[ \sigma _ 1 ( d ) \le a \right] \sigma _ 1 ( d ) \sum _ { i = 1 } ^ { \left\lfloor n / d \right\rfloor } \sum _ { j = 1 } ^ { \left\lfloor m / d \right\rfloor } \sum _ { k \mid i, k\mid j} \mu ( k )
\\
= &
\sum _ { d = 1 } \left[ \sigma _ 1 ( d ) \le a \right] \sigma _ 1 ( d ) \sum _ { k =1} \mu ( k ) \left\lfloor \dfrac{ n }{ k d } \right\rfloor \left\lfloor \dfrac{ m }{ k d } \right\rfloor
\\
= &
\sum _ {T = 1} \left( \sum _ { d \mid T } \left[ \sigma _ 1 ( d ) \le a \right] \sigma _ 1 ( d ) \mu \left( \dfrac{T}{d} \right) \right) \left\lfloor \dfrac{ n }{ T } \right\rfloor \left\lfloor \dfrac{ m }{ T } \right\rfloor
\end{aligned}
\]
如果处理好括号内的函数的前缀和求取,我们就可以使用整除分块了。
好像有点难搞。
智商不够,DS 来凑。
离线处理这个题,先把输入数据按 \(a\) 的大小排序。
预处理的时候把 \(\sigma _ 1 (d)\) 的每个点的值放入一个小根堆中。
每次求取一个输入数据的答案时,先从小根堆中取出不超过 \(a\) 的点,按照一般的方法点值修改树状数组即可。
最后的时间复杂度就是 \(O( n \ln ^ 2 n + T \sqrt{n} \ln n )\)。
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
namespace Ehnaev{
inline ll read() {
ll ret=0,f=1;char ch=getchar();
while(ch<48||ch>57) {if(ch==45) f=-f;ch=getchar();}
while(ch>=48&&ch<=57) {ret=(ret<<3)+(ret<<1)+ch-48;ch=getchar();}
return ret*f;
}
inline void write(ll x) {
static char buf[22];static ll len=-1;
if(x>=0) {do{buf[++len]=x%10+48;x/=10;}while(x);}
else {putchar(10);do{buf[++len]=-(x%10)+48;x/=10;}while(x);}
while(len>=0) putchar(buf[len--]);
}
}using Ehnaev::read;using Ehnaev::write;
inline void writeln(ll x) {write(x);putchar(10);}
const ll mo=(1ll<<31),N=1e5,M=2e4;
ll T,cnt;
ll mu[N+5],prime[N+5],sgm1[N+5],c[N+5];
bool ff[N+5];
struct Qry{
ll n,m,a,id,ans;
}a[M+5];
struct node{
ll val,id;
inline bool operator > (const node& rhs) const {
return val>rhs.val;
}
}t;
priority_queue<node,vector<node>,greater<node> > q;
inline bool cmp1(Qry x,Qry y) {return x.a<y.a;}
inline bool cmp2(Qry x,Qry y) {return x.id<y.id;}
inline void Init() {
for(ll i=1;i<=N;i++) {
for(ll j=i;j<=N;j+=i) {
sgm1[j]=(sgm1[j]+i)%mo;
}
}
for(ll i=1;i<=N;i++) {t.val=sgm1[i];t.id=i;q.push(t);}
ff[1]=1;mu[1]=1;
for(ll i=2;i<=N;i++) {
if(!ff[i]) {prime[++cnt]=i;mu[i]=mo-1;}
for(ll j=1;j<=cnt&&i*prime[j]<=N;j++) {
ff[i*prime[j]]=1;
if(i%prime[j]==0) {
mu[i*prime[j]]=0;break;
}
mu[i*prime[j]]=mu[i]*mu[prime[j]]%mo;
}
}
}
inline void Add(ll x,ll y) {for(;x<=N;x+=x&-x) c[x]=(c[x]+y)%mo;}
inline ll Ask(ll x) {ll r=0;for(;x;x-=x&-x) r=(r+c[x])%mo;return r;}
inline void Init_(ll r) {
while(!q.empty()&&q.top().val<=r) {
ll ti=q.top().id,tv=q.top().val;
for(ll i=ti,cn=1;i<=N;i+=ti,cn++) {
ll tmp=tv*mu[cn]%mo;if(tmp) Add(i,tmp);
}
q.pop();
}
}
int main() {
T=read();
for(ll i=1;i<=T;i++) {
a[i].n=read();a[i].m=read();a[i].a=read();a[i].id=i;
}
sort(a+1,a+T+1,cmp1);
Init();
for(ll ii=1;ii<=T;ii++) {
Init_(a[ii].a);
ll n=a[ii].n,m=a[ii].m;if(n<m) swap(n,m);
for(ll i=1,j;i<=m;i=j+1) {
j=min(n/(n/i),m/(m/i));
ll tmp=(((Ask(j)-Ask(i-1)+mo)%mo)*(n/i)%mo)*(m/i)%mo;
a[ii].ans=(a[ii].ans+tmp)%mo;
}
}
sort(a+1,a+T+1,cmp2);
for(ll i=1;i<=T;i++) {writeln(a[i].ans);}
return 0;
}