bzoj3701 Olympic Games

Codeforces要举办一场计算机比赛,主办方将所有人安排在一个nm的网格图的点上(也就是总共有(n+1)(m+1)个人参加了本次比赛),然后主办方给每个人都配备了一台计算机(也就是说n*m的网格图的每个格点上都有一台计算机,计算机的体积忽略不计)。
但是主办方由于没钱或者是其它原因,不能给计算机配备挡板(主办方:能准备这么多台计算机就不错了),因此一些人就可以直接看到别人的计算机屏幕(当然肯定会先装作四处看风景然后顺便看看啦2333),一个人要看到其他人的计算机屏幕当且仅当:
1、这两个人的连线之间没有其它计算机遮挡(这个原因是显然的吧)。
2、两个人之间的欧氏距离在[l,r]之间,因为如果两个人距离太近,那么一个人看另一个人的计算机屏幕时就很容易被发现,而两个人距离太远的话就看不到了。
由以上两个条件可以很容易地知道,如果A能看到B的计算机屏幕,那么B也能看到A的计算机屏幕。
现在主办方想做好反作弊的准备,第一步就是求出满足A和B都能看到对方的计算机屏幕的(A,B)的对数,由于这个结果可能很大,你只需要输出其模mod的值。
注:(A,B)和(B,A)视为相同。
对于100%的数据,n,m<=100000,l,r<=150000,mod<=1000000000

这个题。。。。。。
发现欧氏距离这个限制我们并不能用一些很好的方法解决。(圆内格点数还有待研究)
然后就枚举一维的距离x,然后可以用sqrt计算y的最大值。(注意sqrt相对O(1)是很慢的,谨慎使用)
然后发现就是知道y的最大值,求x与y互质的一些贡献。
然后就可以莫比乌斯反演。
sigma(x , (n-x+1) * sigma(y , [gcd(x,y)==1] * (m-y+1)) )
=sigma(x , (n-x+1) * sigma(d|x , mu[d] * sigma(d|y, m-y+1)))
后面那个用等差数列O(1)求和,d是因数只有O(sqrt(n))个,直接折半枚举就行。
注意用差分时小心精度,可以先平方。
ps:好像bzoj的数据很水的样子。

AC Code:

#include<bits/stdc++.h>
#define maxn 100005
#define LL long long
using namespace std;

int n,m,l,r,mod;
int mu[maxn],pr[maxn],cnt_pr;
bool vis[maxn];
void sieve()
{
	mu[1] = 1;
	for(int i=2;i<maxn;i++)
	{
		if(!vis[i]) pr[cnt_pr++]=i,mu[i]=-1;
		for(int j=0;j<cnt_pr && pr[j] * i < maxn;j++)
		{
			vis[i * pr[j]] = 1;
			if(i % pr[j] == 0) break;
			mu[i * pr[j]] = -mu[i];
		}
	}
}

int solve(LL Lensq)
{
	int ret = 0;
	for(int x=1,limx=min(1.0*n,floor(sqrt(Lensq)));x<=limx;x++)
	{
		int sum = 0 , limy = min(1.0*m,floor(sqrt(Lensq - 1ll * x * x)));
		for(int y=1,res;y*y<=x;y++)
			if(x % y == 0){
				res = x / y;
				sum = (sum + 1ll * mu[y] * (m-y+1+m-y*(limy/y)+1) * (limy/y)) % mod;
				if(y!=res)
					sum = (sum + 1ll * mu[res] * (m-res+1+m-res*(limy/res)+1) * (limy/res)) % mod;
			}
		ret = (ret + 1ll * sum * (n-x+1)) % mod;
	}
	if(Lensq >= 1) ret = (ret + 1ll *n * (m+1) % mod + 1ll *(n+1) * m % mod) % mod;
	return ret;
}

int main()
{
	sieve();
	scanf("%d%d%d%d%d",&n,&m,&l,&r,&mod);
	printf("%d\n",(solve(1ll*r*r)-solve(1ll*l*l-1)+mod)%mod);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值