【BZOJ 3529】 [Sdoi2014]数表 (莫比乌斯+分块+离线+树状数组)

3529: [Sdoi2014]数表

Description

    有一张N×m的数表,其第i行第j列(1 < =i < =礼,1 < =j < =m)的数值为
能同时整除i和j的所有自然数之和。给定a,计算数表中不大于a的数之和。

Input

    输入包含多组数据。
    输入的第一行一个整数Q表示测试点内的数据组数,接下来Q行,每行三个整数n,m,a(|a| < =10^9)描述一组数据。

Output

    对每组数据,输出一行一个整数,表示答案模2^31的值。

Sample Input

2
4 4 3
10 10 5

Sample Output

20
148
 
 
【分析】
  
  先假设没有a的限制。。
  设g(i)=ΣΣ[(x,y)==i](1<=x<=n,1<==y<=m)
     g(i)=Σmu[d]*(n/(i*d))*(m/(i*d))
     尝试把i和d分开,设D=i*d
  那么g(i)=(n/D)*(M/D)*Σmu[i/D]  (i|D)
  设f(i)为i的约数和,这个可以nlogn求出来,那么ans=g(i)*f(i)=(n/D)*(M/D)*Σmu[i/D]*f(i) (i|D)
 
  对于a的限制,是说f(i)<=a的东西对ans有贡献,其他的没有贡献。
  所以就离线一下~~按照f(i)排序,询问按照a排序,因为要询问前缀和,所以当他开始有贡献的时候就把他放到树状数组里面去。
  剩下的就是分块处理了。
 
  Mod的地要自然溢出最后取模?不然会超时,像我一样~
 
 
  别人的题解:

 
 
代码如下:
  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 #include<queue>
  7 #include<cmath>
  8 using namespace std;
  9 #define Maxn 100010
 10 #define LL unsigned int
 11 #define INF 0xfffffff
 12 LL mx;
 13 LL Mod;
 14 
 15 int mu[Maxn];
 16 LL pri[Maxn],pl;
 17 bool q[Maxn];
 18 
 19 
 20 struct node
 21 {
 22     LL x,y,a,ans,id;
 23 }t[Maxn],f[Maxn];
 24 
 25 LL mymin(LL x,LL y) {return x<y?x:y;}
 26 LL mymax(LL x,LL y) {return x>y?x:y;}
 27 
 28 bool cmp(node x,node y) {return x.a<y.a;}
 29 bool cmp2(node x,node y) {return x.id<y.id;}
 30 
 31 void get_mu()
 32 {
 33     pl=0;
 34     memset(q,1,sizeof(q));
 35     mu[1]=1;
 36     for(LL i=2;i<=mx;i++)
 37     {
 38         if(q[i])
 39         {
 40             pri[++pl]=i;
 41             mu[i]=-1;
 42         }
 43         for(LL j=1;j<=pl;j++)
 44         {
 45             if(i*pri[j]>mx) break;
 46             q[i*pri[j]]=0;
 47             if(i%pri[j]==0) mu[i*pri[j]]=0;
 48             else mu[i*pri[j]]=-mu[i];
 49             if(i%pri[j]==0) break;
 50         }
 51     }
 52     for(LL i=1;i<=mx;i++) f[i].a=0;
 53     for(LL i=1;i<=mx;i++)
 54      for(LL j=i;j<=mx;j+=i)
 55         f[j].a=f[j].a+i;
 56     for(LL i=1;i<=mx;i++) f[i].id=i;
 57 }
 58 
 59 LL c[Maxn],as[Maxn];
 60 
 61 LL add(LL x,LL y)
 62 {
 63     as[x]+=y;
 64     for(LL i=x;i<=mx;i+=i&(-i))
 65         c[i]+=y;
 66 }
 67 
 68 LL get_sum(LL x)
 69 {
 70     LL ans=0;
 71     for(LL i=x;i>=1;i-=i&(-i))
 72       ans+=c[i];
 73     return ans;
 74 }
 75 
 76 void change(LL x)
 77 {
 78     LL now=f[x].id;
 79     for(LL i=now;i<=mx;i+=now)
 80       add(i,(LL)(mu[i/now]*f[x].a));
 81 }
 82 
 83 
 84 LL get_ans(LL n,LL m)
 85 {
 86     LL ans=0,t;
 87     if(n>m) t=n,n=m,m=t;
 88     
 89     LL sq=(LL)ceil(sqrt((double)m));
 90     for(LL i=1;i<=mymin(sq,n);i++)
 91     {
 92         LL x=(LL)(n/i),y=(LL)(m/i);
 93         ans+=as[i]*(n/i)*(m/i);
 94     }
 95     
 96     
 97     for(LL i=sq+1;i<=n;)
 98     {
 99         LL x=n/i,y=m/i;
100         LL r1=n/x+1,r2=m/y+1;
101         if(r1>n+1) r1=n+1;
102         if(r2>n+1) r2=n+1;
103         LL r=mymin(r1,r2);
104         
105         ans+=(get_sum(r-1)-get_sum(i-1))*x*y;
106         i=r;
107     }
108     
109     return ans;
110 }
111 
112 int main()
113 {
114     Mod=1;
115     for(LL i=1;i<=31;i++) Mod*=2;
116     
117     LL T;
118     T=1;
119     scanf("%d",&T);
120     
121     mx=0;
122     
123     for(LL i=1;i<=T;i++)
124     {
125         scanf("%d%d%d",&t[i].x,&t[i].y,&t[i].a);
126         mx=mymax(mx,mymin(t[i].x,t[i].y));
127         t[i].id=i;
128     }
129     sort(t+1,t+1+T,cmp);
130     get_mu();
131     sort(f+1,f+1+mx,cmp);
132     
133     memset(c,0,sizeof(c));
134     memset(as,0,sizeof(as));
135     LL now=1;
136     for(LL i=1;i<=T;i++)
137     {
138         while(f[now].a<=t[i].a&&now<=mx) change(now),now++;
139         t[i].ans=get_ans(t[i].x,t[i].y);
140     }
141     sort(t+1,t+1+T,cmp2);
142     
143     for(LL i=1;i<=T;i++)
144     {
145         printf("%d\n",(t[i].ans%Mod+Mod)%Mod);
146     }
147     return 0;
148 }
[BZOJ 3529]

 

2016-09-03 10:57:57

 
posted @ 2016-09-03 10:54  konjak魔芋  阅读(193)  评论(0编辑  收藏  举报