題意:
一張n*m的表格,( i , j )的格子上的數爲gcd( i , j ) 的約數和,詢問所有格子的不超過a的數之和,T組數據。
1<=n,m<=1e5,|a|<=1e9,T<=2e4
思路:
這題很多細節不是很容易想到。。
爲了方便表述,定義:
F(x):x的約數和,可以預處理得到
G(x):滿足題目的gcd==x的組數
我們先分析g[i]:
得到:
這個看上去已經很快了。。但是還是會TLE,繼續優化:
到這裏發現後面的部分是個狄利克雷卷積,可以利用樹狀數組維護,這樣就可以在的時間內完成一次查詢
那麼這部分的更新的總複雜度爲,屬於可以接受的時間複雜度
爲了方便統計答案的時候不考慮大於a的格子,考慮離線處理
將所有詢問以a進行排序,每次詢問將所有小於a的f(x)的所有倍數插入到樹狀數組中。
那麼總的複雜度就爲:
代碼:
#include<bits/stdc++.h>
#define lowbit(x) (x&(-x))
const int N = 1e5+10;
const int mod = (1LL<<31)-1;
using namespace std;
bool Np[N];
int mu[N],A[N],valE[N],inde[N],ID[N],ans[N];
vector<int>pr;
inline void add(int x,int val){
while(x<N){
A[x]+=val;
A[x]&=mod;
x+=lowbit(x);
}
}
inline int query(int x){
int res = 0;
while(x){
res = (res+A[x])&mod;
x -= lowbit(x);
}return res;
}
struct que{
int n,m,a;
}Q[N];
bool cmp1(const int& a,const int& b){
return valE[a]<valE[b];
}
bool cmp2(const int& a,const int& b){
return Q[a].a<Q[b].a;
}
void init(){
mu[1] = 1;
for(int i=2;i<N;i++){
if(!Np[i]){
pr.push_back(i);
mu[i] = -1;
}for(int j=0,k=pr[0]*i;k<N;k=pr[++j]*i){
Np[k] = true;
if(i%pr[j]==0){
mu[k] = 0;
break;
}mu[k] = -mu[i];
}mu[i] += mu[i-1];
}for(int i=1;i<N;i++){
ID[i] = i;
for(int j=i;j<N;j+=i){
valE[j] += i;
}
}sort(ID+1,ID+N,cmp1);
}
int work(int n,int m){
int res = 0 ;
for(int i=1,last;i<=n;i=last+1){
int nn = n/i , mm = m/i;
last = min ( n / nn , m / mm );
res += (query(last)-query(i-1) )*nn*mm;
res &= mod;
}return (res&mod);
}
int main()
{
//freopen("data.in","r",stdin);
//freopen("my_ans.out","w",stdout);
init();
int q;
scanf("%d",&q);
for(int i=1;i<=q;i++){
inde[i] = i;
scanf("%d%d%d",&Q[i].n,&Q[i].m,&Q[i].a);
if(Q[i].n>Q[i].m)swap(Q[i].n,Q[i].m);
}sort(inde+1,inde+q+1,cmp2);
for(int qq=1,pos=1;qq<=q;qq++){
int p = inde[qq];
while(pos<N&&valE[ID[pos]]<=Q[p].a){
for(int i=1,j=ID[pos]*i;j<N;j+=ID[pos],i++)if(mu[i]-mu[i-1]){
add(j,valE[ID[pos]]*(mu[i]-mu[i-1]));
}pos++;
}ans[p] = work(Q[p].n,Q[p].m);
}for(int i=1;i<=q;i++){
printf("%d\n",ans[i]);
}
}