Banner 统计

题目描述:

LZN 搞完保送生考试,终于要回到信息组大家庭了,Chanxer 决定要好好地欢迎LZN,于是他在在操场上整齐地插了(M + 1) *(N + 1) 个标杆,形成了一个平面直角坐标系,左下角的标杆的坐标为(0; 0),右上角的标杆的坐标为(M;N),Chanxer 现在想要选择两个标杆作为端点连上横幅“ 机房欢迎你”。

可是,由于Chanxer 很农,他不希望横幅被其它的标杆拦住,因此他要求选择的两个标杆的连线不应该经过其它标杆,并且横幅的长度还应该在[L;R]以内。

现在Chanxer 想要知道他有多少种选法,注意,由于横幅的两面是一模一样的,所以选择的两个点没有起点终点之分,鉴于答案可能很大,而又不允许上交Python、Java 等语言的源代码,你只需要告诉他答案除以B 的余数是多少就可以了。

题目分析:

我们设两个两个标杆横坐标的距离差为x,纵坐标的距离差为y。
我们把x=0和y=0的情况刨出来单算:
只有L<=1的时候,这两种情况才有答案,答案为n*(m+1)+m *(n+1)

剩下的就是:
这里写图片描述

解释一下这个式子的意思:枚举x和y,答案加上满足长度在范围内且连线不经其他过格点(即gcd(x,y)==1)的方案。
对于一对固定的x和y,它的在横向上最多可以取(m-x+1)个位置,在纵向上最多可以取(n-y+1)个位置,那么可选的方案就共有(m-x+1)*(n-y+1)种,但是这条线段可以向上倾斜也可以向下倾斜,所以答案要乘2。

我们只需要枚举x和y,当然如果暴力枚举的话肯定GG。
观察这个式子,可以观察出,x只需要枚举到min(m,R),而当x固定的时候,y只需要在区间[sqrt(L* L-x *x),sqrt(R *R-x *x) ]内枚举即可,即可以省掉长度范围的判断。
按照上述条件把原式进行变形:
这里写图片描述

我们设 l 为枚举y的下限,r为枚举y的上限。
于是问题转化成了,如何求:
这里写图片描述

即如何快速求出:
这里写图片描述
(lim和x是给定的)

这个时候可以利用莫比乌斯函数进行容斥,即该式可以转化为:
这里写图片描述
上面这个转化不懂的可以选择看看我的Zap的题解:
http://blog.csdn.net/todobe/article/details/60579910
也可以根据莫比乌斯函数求和的性质,只有gcd为1的时候莫比乌斯函数的和为1,其他时候都为0,乘上方案数相加之后恰好为所求答案。

这样我们只要用sqrt(x)的时间枚举x的约数,然后∑(d|y)(n-y+1)是可以O(1)算出的。
不会O(1)算的小伙伴看这里:
我们设一个临时变量t=lim/d(因为是要求lim范围内合法的y嘛)
这个t代表共有t个合法的y,分别为:d,2d,3d……td;
我们要求的就是把这些y分别带入(n-y+1)里面然后求和。
这样我们把常量(n+1)提出来,就是(n+1)* t +((1+t)* t /2)*d
这样就算出来了(你们看完之后是不是觉得写题解的人非常智障啊=。=)

时间复杂度O(nsqrt(n))
其实后面的部分也可以不用莫比乌斯函数来做,直接容斥也可以。
(但是本宝宝是蒟蒻T^T,只会这种)

注意:

在计算第二维的下限的时候要和1取一个max,因为0的情况你已经单算了,如果枚举的x(即我代码里的i)已经大于L的时候,它的下限直接设为1即可

代码如下:

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#define N 120000
using namespace std;
typedef long long LL;
inline LL sqr(LL x) { return x*x; }
LL n,m,L,R,mod,ans;
inline LL sum(LL x) { return (x*(x+1)>>1)%mod; }
bool mark[N];
LL pri[N],u[N];
int top;
void init()
{
    u[1]=1;
    for(LL i=2;i<N;i++)
    {
        if(!mark[i])
        {
            pri[++top]=i;
            u[i]=-1;
        }
        for(int j=1;j<=top && pri[j]*i<N;j++)
        {
            mark[i*pri[j]]=true;
            if(i%pri[j]==0)
            {
                u[i*pri[j]]=0;
                break;
            }
            u[i*pri[j]]=-u[i];
        }
    }
}
LL calc(LL lim,LL x)
{
    LL ans=0;
    LL t,i;
    for(i=1;i*i<x;i++)
    {
        if(x%i==0)
        {
            t=lim/i;
            ans+=(u[i]*((n+1)*t%mod-sum(t)*i%mod)+mod)%mod,ans%=mod;
            t=lim/(x/i);
            ans+=(u[x/i]*((n+1)*t%mod-sum(t)*(x/i)%mod)+mod)%mod,ans%=mod;
        }
    }
    if(i*i==x)
    {
        t=lim/i;
        ans+=(u[i]*((n+1)*t%mod-sum(t)*i%mod)+mod)%mod,ans%=mod;
    }
    return ans;
}
int main()
{
    init();
    cin>>n>>m>>L>>R>>mod;
    for(LL i=1;i<=min(m,R);i++)
    {
        LL l=i>L?1:max(1ll,LL(ceil(sqrt(sqr(L)-sqr(i)))));
        LL r=min(n,LL(sqrt(sqr(R)-sqr(i))));
        if(l<=r) ans+=(calc(r,i)-calc(l-1,i)+mod)%mod*(m-i+1)%mod*2%mod,ans%=mod;
    }
    if(L<=1) ans+=m*(n+1)%mod+n*(m+1)%mod,ans%=mod;
    cout<<ans<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值