分析:
题目所求:
实际上,
h
h
的意义:的约数和
设
f(n)
f
(
n
)
是
n
n
的约数和
随意查了一下,约数和定理:
f(n)=(p00+p11+…pa11)(p02+p12+…pa22)…(p0k+p1k+…pakk) f ( n ) = ( p 0 0 + p 1 1 + … p 1 a 1 ) ( p 2 0 + p 2 1 + … p 2 a 2 ) … ( p k 0 + p k 1 + … p k a k )我们发现当 p,q p , q 互质时, f(pq)=f(p)f(q) f ( p q ) = f ( p ) f ( q ) , f f 是个积性函数
也就是说可以线性筛求解(当然直接暴力求也没什么影响)a a 的限制比较复杂,简单的,我们先来看看如果没有的限制,是个什么情况
∑k=1min(n,m)∑i=1n∑j=1mf(k)[gcd(i,j)=k] ∑ k = 1 m i n ( n , m ) ∑ i = 1 n ∑ j = 1 m f ( k ) [ g c d ( i , j ) = k ]设 g(i) g ( i ) 表示 ∑nx=1∑my=1[gcd(x,y)=i] ∑ x = 1 n ∑ y = 1 m [ g c d ( x , y ) = i ]
g(i)=∑d=1min(n/i,m/i)ndimdiμ(d) g ( i ) = ∑ d = 1 m i n ( n / i , m / i ) n d i m d i μ ( d )则原式:
∑k=1min(n,m)f(k)∑d=1min(n/k,m/k)ndkmdkμ(d) ∑ k = 1 m i n ( n , m ) f ( k ) ∑ d = 1 m i n ( n / k , m / k ) n d k m d k μ ( d )设 Q=dk Q = d k
∑k=1min(n,m)f(k)∑Q=1min(n,m)nQmQμ(Qk)=∑Q=1min(n,m)nQmQ∑k|Qf(k)μ(Qk) ∑ k = 1 m i n ( n , m ) f ( k ) ∑ Q = 1 m i n ( n , m ) n Q m Q μ ( Q k ) = ∑ Q = 1 m i n ( n , m ) n Q m Q ∑ k | Q f ( k ) μ ( Q k )显然 ∑k|Qf(k)μ(Qk) ∑ k | Q f ( k ) μ ( Q k ) 是 f f 函数与函数的狄利克雷卷积
预处理前缀和,前面的 nQmQ n Q m Q 分块,那么这个式子的计算就达到了 n−−√ n那么我们再加上 a a 的限制
离线,将询问按照从小到大排序, f f 函数按照从小到大排序
询问的时候,我们就动态维护一个树状数组
每次处理询问之前,把数值小于当前a的 f f 值加入树状数组
树状数组的每一位存的 ∑d|xf(d)μ(xd) ∑ d | x f ( d ) μ ( x d )
数据组数为 T T ,询问复杂度注意:
这题复杂度有点卡,对于取模运算我们可以对 int i n t 自然溢出,最后再 &2147483647 & 2147483647 就可以了
tip
第一次写完,发现小数据OK,大数据WA了(比标准答案大一点)、
竟然是线性筛mu写错了。。。orz
注意最后的取模,一不小心就会炸掉变成负数
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=100005; int n,m,Q,sshu[N],tot=0,mu[N],ans[N],mx,c[N]; bool no[N]; struct node{ int n,m,A,id; bool operator <(const node &b) const { return A<b.A; } }; node a[N]; struct node2{ int x,id; bool operator <(const node2 &b) const { return x<b.x||(x==b.x&&id<b.id); } }; node2 f[N]; void prepare(int n) { memset(no,0,sizeof(no)); mu[1]=1; for (int i=2;i<=n;i++) { if (!no[i]) { sshu[++tot]=i; mu[i]=-1; } for (int j=1;j<=tot&&sshu[j]*i<=n;j++) { no[i*sshu[j]]=1; if (i%sshu[j]==0) { mu[i*sshu[j]]=0; break; } mu[i*sshu[j]]=-mu[i]; } } for (int i=1;i<=n;i++) //暴力求f for (int j=i;j<=n;j+=i) f[j].x+=i; for (int i=1;i<=n;i++) f[i].id=i; } void add(int x,int z) {for (int i=x;i<=mx;i+=(i&(-i))) c[i]+=z;} int ask(int x) {int ans=0; for (int i=x;i>0;i-=(i&(-i))) ans+=c[i]; return ans;} void solve(int _) { int n=a[_].n,m=a[_].m; int last=0; for (int i=1;i<=min(n,m);i=last+1) { last=min(n/(n/i),m/(m/i)); ans[a[_].id]+=(n/i)*(m/i)*(ask(last)-ask(i-1)); } } int main() { mx=0; scanf("%d",&Q); for (int i=1;i<=Q;i++) { scanf("%d%d%d",&a[i].n,&a[i].m,&a[i].A); a[i].id=i; mx=max(mx,min(a[i].n,a[i].m)); } prepare(mx); sort(a+1,a+1+Q); sort(f+1,f+1+mx); int now=1; for (int i=1;i<=Q;i++) { while (now<=mx&&f[now].x<=a[i].A) { for (int j=f[now].id;j<=mx;j+=f[now].id) //sum(k|Q) f(k)mu(Q/k) 所有k的倍数Q都需要add add(j,f[now].x*mu[j/f[now].id]); //卷积 now++; } solve(i); } for (int i=1;i<=Q;i++) printf("%d\n",ans[i]&((1<<31)-1)); return 0; }