bzoj 3529 [Sdoi2014]数表

3529: [Sdoi2014]数表

Time Limit: 10 Sec   Memory Limit: 512 MB
Submit: 1832   Solved: 922
[ Submit][ Status][ Discuss]

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

HINT

1 < =N.m < =10^5  , 1 < =Q < =2×10^4




【分析】

有点神啊...莫比乌斯反演+树状数组。

https://wenku.baidu.com/view/fbec9c63ba1aa8114431d9ac.html (Orz PoPoQQQ)

悲伤的发现自己几乎无法做出来一道反演题...(蒟蒻能看懂题解就不错了

总的来说就是暴力更新的时候不能把整个前缀和数组更新掉,要用一只树状数组维护= =



【代码】

//bzoj 3529 数表 
#include<algorithm>
#include<iostream>
#include<cstring>
#include<climits>
#include<cstdio>
#define N 100000
#define p INT_MAX
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
const int mxn=100005;
int T;
bool vis[mxn];
int n[mxn],m[mxn],c[mxn];
int pri[mxn],miu[mxn],ans[mxn];
struct node {int pos,v;} f[mxn]; 
struct query {int n,m,a,id;} q[mxn];
inline bool comp_n(node x,node y)
{
	return x.v<y.v;
}
inline bool comp_q(query x,query y)
{
	return x.a<y.a;
}
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x*f; 
}
inline void shai()
{
	miu[1]=1;
	fo(i,2,N)
	{
		if(!vis[i]) pri[++pri[0]]=i,miu[i]=-1;
		for(int j=1;j<=pri[0] && i*pri[j]<=N;j++)
		{
			vis[i*pri[j]]=1;
			if(i%pri[j]==0)
			{
				miu[i*pri[j]]=0;
				break;
			}
			miu[i*pri[j]]=-miu[i];
		}
	}
	fo(i,1,N)
	{
		f[i].pos=i;
	    for(int j=i;j<=N;j+=i)
	      f[j].v+=i;
	}
	sort(f+1,f+N+1,comp_n);
}
inline int lowbit(int x) {return x&-x;}
inline void add(int x,int v)
{
	for(int i=x;i<=N;i+=lowbit(i))
	  c[i]+=v;
}
inline int getsum(int x)
{
	int sum=0;
	for(int i=x;i>0;i-=lowbit(i))
	  sum+=c[i];
	return sum;
}
inline int solve(int n,int m)
{
	int tmp=0;
	for(int i=1,last=0;i<=n;i=last+1)
	{
		last=min(n/(n/i),m/(m/i));
		tmp+=(n/i)*(m/i)*(getsum(last)-getsum(i-1));
	}
	return tmp&p;
}
int main()
{
	shai();
	T=read();
	fo(i,1,T)
	{
		q[i].n=read(),q[i].m=read(),q[i].a=read(),q[i].id=i;
		if(q[i].n>q[i].m) swap(q[i].n,q[i].m);
	}
	sort(q+1,q+T+1,comp_q);
	int now=1;
	fo(i,1,T)
	{
		while(now<=N && f[now].v<=q[i].a)
		{
			for(int j=1;j*f[now].pos<=N;j++)
			  add(j*f[now].pos,f[now].v*miu[j]);
			now++;
		}
		ans[q[i].id]=solve(q[i].n,q[i].m);
	}
	fo(i,1,T) printf("%d\n",ans[i]&p); 
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值