您的位置:首页 > 其它

【bzoj3529】[Sdoi2014]数表 线性筛法+树状数组+莫比乌斯反演+数论分块

2016-03-31 10:55 423 查看


后面那一块暴力做出来就可以了,复杂度是O(n log n)的

考虑a的限制,因为f(i)<=a时才计入答案,那么我们可以离线处理,对询问按照a排序,每次插入小于a的所有的f(i),那么查询前缀和就用树状数组来搞就可以了。

因为本题一共只有100000个左右的f(i),所以可以这么做。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#define maxn 100010

using namespace std;

struct yts
{
int n,m,a,id;
}q[20100];

struct yts1
{
int id,data;
}seq[maxn];

int T,tot;
int prime[maxn];
int mu[maxn],c[maxn],ans[maxn];
bool vis[maxn];

bool cmp(yts1 x,yts1 y)
{
return x.data<y.data;
}

bool cmp1(yts x,yts y)
{
return x.a<y.a;
}

int lowbit(int x)
{
return x&(-x);
}

int query(int x)
{
int ans=0;
for (int i=x;i;i-=lowbit(i)) ans+=c[i];
return ans;
}

void add(int i,int x)
{
for (;i<=100000;i+=lowbit(i)) c[i]+=x;
}

int cal(int n,int m)
{
int last=0,ans=0;
if (n>m) swap(n,m);
for (int i=1;i<=n;i=last+1)
{
last=min(n/(n/i),m/(m/i));
ans+=(n/i)*(m/i)*(query(last)-query(i-1));
}
return ans;
}

int main()
{
scanf("%d",&T);
mu[1]=1;
for (int i=2;i<=100000;i++)
{
if (!vis[i])
{
prime[++tot]=i;
mu[i]=-1;
}
for (int j=1;j<=tot && i*prime[j]<=100000;j++)
{
vis[i*prime[j]]=1;
if (i%prime[j]==0)
{
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]=-mu[i];
}
}
for (int i=1;i<=100000;i++)
{
seq[i].id=i;
for (int j=1;j*i<=100000;j++) seq[i*j].data+=i;
}
sort(seq+1,seq+100000+1,cmp);
for (int i=1;i<=T;i++) {scanf("%d%d%d",&q[i].n,&q[i].m,&q[i].a);q[i].id=i;}
sort(q+1,q+T+1,cmp1);
memset(c,0,sizeof(c));
int tmp=0;
for (int i=1;i<=T;i++)
{
while (tmp<100000 && seq[tmp+1].data<=q[i].a)
{
tmp++;
for (int j=1;j*seq[tmp].id<=100000;j++) add(j*seq[tmp].id,mu[j]*seq[tmp].data);
}
ans[q[i].id]=cal(q[i].n,q[i].m);
}
for (int i=1;i<=T;i++) printf("%d\n",ans[i]&0x7fffffff);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: