[SDOI2014]数表
一.题意简述
题目要求的是
[sumlimits_{i=1}^{n}sumlimits_{j=1}^{m}sigma(gcd(i,j))<a\
]
那个是除数函数啦。(不知道的自己百度)
二.解题思路
有什么思路,干就完了,奥利给!!这个(<a)很碍眼,反手扔了不虚他。于是要求解
[sumlimits_{d=1}^{n}sumlimits_{i=1}^{lfloorfrac{n}{d}
floor}
sumlimits_{j=1}^{lfloorfrac{m}{d}
floor}sigma(d)[gcd(i,j)=1]\
]
虽说这一步跨度有点大,但是是可以理解的,对于大多数gcd都会选择起手枚举gcd的值,而这里的(i,j)相当于是系数一样的东西叭。由于是枚举的最大公约数,所以(i,j)要互质才好,这是常规操作。
现在发现了一只野生的(epsilon),(如果读不懂这句话请先看这篇文章),那么就把他换成(mu).(基操)
[sumlimits_{d=1}^{n}sumlimits_{i=1}^{lfloorfrac{n}{d}
floor}
sumlimits_{j=1}^{lfloorfrac{m}{d}
floor}sigma(d)sumlimits_{k|gcd(i,j)}^{}mu(k)\
]
然后走一波换位+基操
[sumlimits_{d=1}^{n}sigma(d)sumlimits_{k=1}^{lfloorfrac{n}{d}
floor}mu(k)
lfloorfrac{n}{dk}
floorlfloorfrac{m}{dk}
floor\
]
其实这里也是在枚举gcd,跟上面思路差不多,再不想让d干扰,换了个位置。
[(p=dk)sumlimits_{p=1}^{n}lfloorfrac{n}{p}
floorlfloorfrac{m}{p}
floorsumlimits_{d|p}^{}mu(d)sigma(frac{p}{d})
]
乘积形式的分母不好看,换一手元,交换位置。
本来这样就好了,但是题目有个a。于是想着怎么把他找回来,加进去吧
[(p=dk)sumlimits_{p=1}^{n}lfloorfrac{n}{p}
floorlfloorfrac{m}{p}
floorsumlimits_{d|p}^{}mu(d)sigma(frac{p}{d})\
sumlimits_{d|p}^{}mu(d)sigma(frac{p}{d})<a
]
这个应该能看懂,首先以求和符号作为分界记后面的那一堆为(g(p))好了,然后考虑一下(mu)函数的性质,了解到(sigma(frac{p}{d})<=a)才对答案有贡献,然而又有多组数据,并且除数函数值的大小跟参数大小没有什么关系,是乱的。(开始暴躁)
不过不慌,我们把(sigma)和a都拉来排个序,新的a出现时便用符合条件的(sigma)去更新(g),再用数论分块算一算。(还是看代码吧)
三.CODE
#define ll long long
#define m_for(i,a,b) for(int i=a;i<=b;++i)
#define lowbit(x) x&(-x)
inline int read() {
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9') {
if(ch=='-')w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
const int MAXN=1e5+1,MAXQ=2*1e4+1;
ll mod=1LL<<31;
int prime[MAXN],tot,mu[MAXN];
ll g[MAXN];
pair<int,int> sigma[MAXN];
bool vis[MAXN];
void m_p(int x){
sigma[1].first=sigma[1].second=1;
mu[1]=1;
m_for(i,2,x){
sigma[i].first++;
if(!vis[i])prime[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&i*prime[j]<=x;++j){
vis[i*prime[j]]=1;
if(i%prime[j]==0)break;
mu[i*prime[j]]=-mu[i];
}
for(int j=i;j<=x;j+=i)sigma[j].first=(sigma[j].first+i)%mod;
sigma[i].second=i;
}
sort(sigma+1,sigma+x+1);
}
struct alf{
int n,m,a,id;
}que[MAXQ];
bool cmp(alf c,alf b){
return c.a<b.a;
}
int tree_g[MAXN];
void change(int x,int y){
for(;x<MAXN;x+=lowbit(x))tree_g[x]+=y;
}
ll sum(int x){
ll res=0;
for(;x>0;x-=lowbit(x))res+=tree_g[x];
return res;
}
ll solve(int n,int m){
if(n>m)swap(n,m);
ll res=0;
for(int l=1,r;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));
res+=1LL*(n/l)*(m/l)*(sum(r)-sum(l-1));
res%=mod;
}
return (res%mod+mod)%mod;
}
int q,res=1;
int ans[MAXQ];
int main(){
m_p(MAXN);
q=read();
m_for(i,1,q)que[i].id=i,que[i].n=read(),que[i].m=read(),que[i].a=read();
sort(que+1,que+q+1,cmp);
m_for(i,1,q){
while(sigma[res].first<=que[i].a&&res<=MAXN){
for(int j=1;j*sigma[res].second<=MAXN;++j)
change(j*sigma[res].second,mu[j]*sigma[res].first);
res++;
}
ans[que[i].id]=solve(que[i].n,que[i].m)%mod;
}
m_for(i,1,q)cout<<ans[i]<<endl;
return 0;
}