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

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

后面那一块暴力做出来就可以了,复杂度是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;
}


你可能感兴趣的:(【bzoj3529】[Sdoi2014]数表 线性筛法+树状数组+莫比乌斯反演+数论分块)