XJOI 夏令营501-511NOIP训练17 蛇形数阵

话说小X在孩提时,都会做标准的蛇形矩阵了,发现很好玩。现在的小X很想对其进行改版,变为如下类型的一个无限大蛇形数阵:
1.jpg
令S(x)表示以1为左上角,x为右下角的矩形内所有数之和。例如S(12)就是具有深色背景的数之和。
给定n,对于“以1为左上角,n为右下角的矩形”内的每一个数i,计算所有S(i)之和。例如,当n=8时,所求结果为S(1)+S(2)+S(9)+S(4)+S(3)+S(8)=1+3+12
+5+10+27=58。

输入格式:

输入文件仅包含一个整数n

输出格式:

输出所求结果除以1,000,000,007的余数。。

样例输入:

12

样例输出:

282

数据范围:

对于20%的数据满足1<=n<=102;
对于40%的数据满足1<=n<=5000;
对于60%的数据满足1<=n<=106;
对于100%的数据满足1<=n<=1010。

时间限制:

1s

空间限制:

256m
 

数论

这道题O(n)的算法是很好想的,只要模拟n次,将数字为n的坐标和矩阵中每个数和位置处理出来

然后可以发现对于矩阵来说,每个数被累加到的次数为这一点到矩阵右下角的子矩阵大小

即$ans=\sum_{i}\sum_{j}a_{i,j}*(x-i+1)*(y-j+1)$

1.jpg

比如12的矩阵中,8被累加的次数为4次

那么就可以统计答案了

但O(n)的算法不足以解决这道题

可以发现可以在O(1)的时间内处理出一个数的位置

那么可以将这个矩阵划分成一个在这个矩阵中最大的正方形和若干条宽度为一的矩形

一个正方形也可以划分成几个“7”字型,那么对于这个"7"字形和宽度为一的矩形可以O(1)的处理出答案

那么总复杂度为$O(\sqrt{n})$

那么“7”字形公式推导过程如下

 

此处仅以奇数的正方形的"7"为例

设当前为第$i$个正方形,整个矩阵的右下角为$(x,y)$

右上的数为$i^{2}$坐标$(i,i)$,左下的数为$(i-1)^{2}+1$,坐标$(i,1)$,中间拐点处为右上和左下数的平均数,坐标$(i,i)$

那么将这个"7"切成一条竖的和一条横着边

1.jpg

如9所在的"7"字形,将其切成7-9和5-7两个边

那么下条边答案为

$((i-1)^{2}+1)*(x-i+1)*y+((i-1)^{2}+1+1)*(x-i+1)*(y-1)+...+((i-1)^{2}+1+i-1)*(x-i+1)*(y-i+1)$

设$s=(i-1)^{2}+1$

$s*(x-i+1)*y+(s+1)*(x-i+1)*(y-1)+...+(s+i-1)*(x-i+1)*(y-i+1)$

$(x-i+1)*[s*y+(s+1)*(y-1)+...+(s+i-1)*(y-i+1)]$

$(x-i+1)*[i*s*y+\sum_{j=1}^{i-1}(y-s)-\sum_{k=1}^{i-1}k^{2}]$

$(x-i+1)*[i*s*y+\frac{i*(i-1)}{2}(y-s)-\frac{i*(i-1)*(2*i-1)}{6}]$

即为公式,那么上条边同理

$(y-i+1)*[i*s*x-\frac{i*(i-1)}{2}(x+s)+\frac{i*(i-1)*(2*i-1)}{6}]$

$s=i^{2}$

因为统计了中间拐点的答案两次

最后再减去中间拐点的答案即可

其他同理,可以推出相同形式的公式

#include <bits/stdc++.h>
#define ll long long
#define mod (ll)1000000007
using namespace std;
ll n,m,ans;
ll findx(ll num)
{
    ll s;
    s=(ll)sqrt(num);
    if (s*s==num)
    {
        if (s&1)
          return 1;
        else
          return s;
    }
    ll mid;
    s++;
    mid=((s-1)*(s-1)+1+s*s)>>1;
    if (num>=mid)
    {
        if (s&1)
          return s-(num-mid);
        else
          return s;
    }
    else
    {
        if (s&1)
          return s;
        else
          return s-(mid-num);
    }
}
ll findy(ll num)
{
    ll s;
    s=(ll)sqrt(num);
    if (s*s==num)
    {
        if (s&1)
          return s;
        else
          return 1;
    }
    ll mid;
    s++;
    mid=((s-1)*(s-1)+1+s*s)>>1;
    if (num>=mid)
    {
        if (s&1)
          return s;
        else
          return s-(num-mid);
    }
    else
    {
        if (s&1)
          return s-(mid-num);
        else
          return s;
    }
}//O(1)的时间处理出坐标
int main()
{
    scanf("%lld",&n);
    ll x,y;
    x=findx(n);y=findy(n);
    m=min(x,y);
    for (ll i=m;i>=1;i--)
    {
        ll mid;
        mid=((i-1)*(i-1)+1+i*i)>>1;
        if (i&1)
        {
            ll s;
            s=i*i;
            s%=mod;
            ans=ans+(i*x%mod*s%mod+i*(i-1)*(2*i-1)/6-i*(i-1)/2*(s+x)%mod+mod)%mod*(y-i+1)%mod;
            ans%=mod;
            s=i*i-2*i+2;
            s%=mod;
            ans=ans+(i*s%mod*y%mod-i*(i-1)*(2*i-1)/6+i*(i-1)/2*(y-s)%mod+mod)%mod*(x-i+1)%mod;
            ans%=mod;
            ans=(ans-mid*(x-i+1)%mod*(y-i+1)%mod+mod)%mod;
        }
        else
        {
            ll s;
            s=i*i-2*i+2;
            s%=mod;
            ans=ans+(i*s%mod*x%mod-i*(i-1)*(2*i-1)/6+i*(i-1)/2*(x-s)%mod+mod)%mod*(y-i+1)%mod;
            ans%=mod;
            s=i*i;
            s%=mod;
            ans=ans+(i*y%mod*s%mod+i*(i-1)*(2*i-1)/6-i*(i-1)/2*(s+y)%mod+mod)%mod*(x-i+1)%mod;
            ans%=mod;
            ans=(ans-mid*(x-i+1)%mod*(y-i+1)%mod+mod)%mod;//减去中间拐点的值
        }
        ans=(ans+mod)%mod;
    }
    for (ll i=m+1;i<=y;i++)
    {
        if (i&1)
        {
            ll s;
            s=i*i;
            s%=mod;
            ans=ans+(x*x%mod*s%mod+x*(x-1)*(2*x-1)/6-x*(x-1)/2*(s+x)%mod+mod)%mod*(y-i+1)%mod;
            ans%=mod;
        }
        else
        {
            ll s;
            s=i*i-2*i+2;
            s%=mod;
            ans=ans+(x*s%mod*x%mod-x*(x-1)*(2*x-1)/6+x*(x-1)/2*(x-s)%mod+mod)%mod*(y-i+1)%mod;
            ans%=mod;
        }
        ans=(ans+mod)%mod;
    }
    for (ll i=m+1;i<=x;i++)
    {
        if (i&1)
        {
            ll s;
            s=(i*i-2*i+2);
            s%=mod;
            ans=ans+(y*s%mod*y%mod-y*(y-1)*(2*y-1)/6+y*(y-1)/2*(y-s)%mod+mod)%mod*(x-i+1)%mod;
            ans%=mod;
        }
        else
        {
            ll s;
            s=i*i;
            s%=mod;
            ans=ans+(y*y%mod*s%mod+y*(y-1)*(2*y-1)/6-y*(y-1)/2*(s+y)%mod+mod)%mod*(x-i+1)%mod;
            ans%=mod;
        }
        ans=(ans+mod)%mod;
    }
    printf("%lld\n",(ans+mod)%mod);
}

 

转载于:https://www.cnblogs.com/huangchenyan/p/11285307.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值