0
点赞
收藏
分享

微信扫一扫

P3312 [SDOI2014] 数表

​​传送门​​

先来一波常规操作 

P3312 [SDOI2014] 数表_前缀和

然后再来一波经常使用的"换一个枚举"

P3312 [SDOI2014] 数表_i++_02

令   

 P3312 [SDOI2014] 数表_前缀和_03

发现我们需要处理 g[d] 的前缀和, 又要保证当前的F[k]<=a

所以我们将询问按a排序, 然后动态将F[k]<=a 的插入树状数组

考虑一个F[x]对哪些有贡献

F[x] 对 g[x] 有 F[x] * mu[1]的贡献

F[x] 对 g[x*2] 有 F[x] * mu[2] 的贡献

然后暴力枚举倍数插入树状数组

#include<bits/stdc++.h>
#define N 100050
using namespace std;
int mu[N],prim[N],isp[N],tot,F[N];
int T; unsigned int ans[N];
struct Quary{
int n,m,val,id;
} q[N];
bool cmp(Quary a,Quary b){return a.val<b.val;}
struct Node{
int val,id;
} A[N];
bool cmp1(Node a,Node b){return a.val<b.val;}
int c[N];
void add(int x,int val){for(;x<=N-50;x+=x&-x) c[x] += val;}
int sum(int x){int ans=0; for(;x;x-=x&-x) ans += c[x]; return ans;}
unsigned int Quary(int n,int m){
if(n>m) swap(n,m);
unsigned int ans = 0;
for(int l=1,r;l<=n;l=r+1){
int v1 = n/l, v2 = m/l;
r = min(n/v1, m/v2);
ans += v1 * v2 * (sum(r) - sum(l-1));
} return ans;
}
int main(){
mu[1] = 1;
for(int i=2;i<=N-50;i++){
if(!isp[i]) prim[++tot] = i, mu[i] = -1;
for(int j=1;j<=tot;j++){
if(i * prim[j] > N - 50) break;
isp[i * prim[j]] = 1;
if(i % prim[j] == 0) break;
mu[i * prim[j]] = -mu[i];
}
}
for(int i=1;i<=N-50;i++)
for(int j=i;j<=N-50;j+=i) F[j] += i;
scanf("%d",&T);
for(int i=1;i<=T;i++) scanf("%d%d%d",&q[i].n, &q[i].m, &q[i].val), q[i].id = i;
for(int i=1;i<=N-50;i++) A[i].id = i, A[i].val = F[i];
sort(q+1, q+T+1, cmp);
sort(A+1, A+N-50+1, cmp1);
int pos = 0;
for(int i=1;i<=T;i++){
while(pos < N-50 && A[pos+1].val <= q[i].val){
++pos;
for(int j=1; j * A[pos].id <= N - 50; j++)
add(j * A[pos].id, mu[j] * A[pos].val);
} ans[q[i].id] = Quary(q[i].n, q[i].m);
}
for(int i=1;i<=T;i++) printf("%d\n",ans[i] & 2147483647);
return 0;
}

 


举报

相关推荐

0 条评论