2017.6.6 数表 思考记录

来源:互联网 发布:阿里java开发手册 pdf 编辑:程序博客网 时间:2024/06/07 03:08

        这个题卡在最后一步了、

       最原始的式子是:


这个d|i 且 d|j   看起来非常和善,是不是可以反演?

但d要求同时能整除i和j,怎么办呢?

由于i和j的最大公因数一定包含所有因数,所以我们可以对gcd(i,j)进行分解,就变成了:



,这样看起来清爽一点:

如果我们从每个位置来统计贡献的话肯定单次就是n^2的

所以我们需要根据每个gcd==?来统计贡献、、 所以设f(j)为gcd==j的贡献

ans=  所有的gcd==?贡献相加           一个gcd==k 的贡献=  gcd==k的个数*gcd==k的约数值和

其中 gcd==k的个数可以反演

就得到了 

这时候再加上a的限制,就是只有g(d)<=a才能被选

如果没有a,就是一个前缀和完事

如果有a,每次前缀和都要变化,所以就可以动态维护一个前缀和(树状数组)

为了方便加入,把问题中的a排序,在把f排序,即可得到答案

码:



#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int mu[100005],qsum[100005],i,tot,su[100005],now,j,n,m,T;bool he[100005];int lowbite(int a){return a&(-a);}void jia(int qi,int zhi){int i;for(i=qi;i<=100000;i+=lowbite(i)){qsum[i]+=zhi;}}int qiu(int zhong){int ans=0,i;for(i=zhong;i;i-=lowbite(i)){ans+=qsum[i];}return ans;}struct F{long long id,a;}f[100005];bool cmp2(F a,F b){return a.a<b.a;}struct wen{int a,ans,id,n1,m1;}w[20005];bool cmp(wen a,wen b){return a.a<b.a;}void eular(){mu[1]=1;for(i=2;i<=100000;i++){   if(!he[i])   {   su[++tot]=i;   mu[i]=-1;      }for(j=1;j<=tot&&i*su[j]<=100000;j++){he[i*su[j]]=1;if(i%su[j]==0){mu[i*su[j]]=0;break;}else mu[i*su[j]]=-mu[i];}}for(i=1;i<=100000;i++){   f[i].id=i;for(j=1;i*j<=100000;j++){f[i*j].a+=i;}}sort(f+1,f+100001,cmp2);//for(i=1;i<=20;i++)cout<<f[i].a<<" ";//cout<<endl;//for(i=1;i<=20;i++)cout<<f[i].id<<" ";}void qzh(int o){    for(i=now+1;f[i].a<=o;i++)    {    now=i;       for(j=1;j*f[i].id<=100000;j++)   jia(f[i].id*j,f[i].a*mu[j]);}}int work(){       int ans=0,lin;for(i=1;i<=n;i=lin+1){  lin=min(n/(n/i),m/(m/i));  ans+=(qiu(lin)-qiu(i-1))*(m/i)*(n/i);}return ans;}int main(){     eular();scanf("%d",&T);for(i=1;i<=T;i++)scanf("%d%d%d",&w[i].n1,&w[i].m1,&w[i].a),w[i].id=i;sort(w+1,w+1+T,cmp);for(int i=1;i<=T;i++){  n=w[i].n1;m=w[i].m1;if(n>m)swap(n,m);qzh(w[i].a);w[w[i].id].ans=work();}for(i=1;i<=T;i++){printf("%d\n",w[i].ans&((1<<31)-1));}} 







原创粉丝点击