HDU-6304 Chiaki Sequence Revisited(找规律)

题目:求数列的前n项和。

每个数出现的次数是log(lowbit(i))+1次,可以二分出a[n]的值,对于同样的次数会构成了一个等差数列。公差就是2^i,项数是n/2^i.

太菜了自己打表没弄出来,参考了https://blog.csdn.net/purple_bro/article/details/81177315写的好详细。

#include<bits/stdc++.h>
using namespace std;
void dabiao()
{
    int a[100],sum[100];
    vector<int>num[100];

    a[1]=a[2]=1;
    sum[1]=1;sum[2]=2;
    for(int i=3;i<100;i++)
    {
        a[i]=a[i-a[i-1]]+a[i-1-a[i-2]];
        sum[i]=sum[i-1]+a[i];
        printf("%2d:%4d%6d\n",i,a[i],sum[i]);
    }
    for(int i=1;i<100;i++)
    {
        int tmp=1,k=a[i];
        while(a[i]%2==0)
        {
            a[i]/=2;
            tmp++;
        }
        num[tmp].push_back(k);
    }
    for(int i=1;i<=8;i++)
    {
        for(int j=0;j<num[i].size();j++)
            printf("%4d",num[i][j]);
        puts("");
    }
}
typedef long long ll;
const ll mod=1e9+7;
ll p[65],n;
ll qmod(ll x,ll p)
{
    ll ans=1;
    while(p)
    {
        if(p&1)
            ans=ans*x%mod;
        x=x*x%mod;
        p>>=1;
    }
    return ans;
}
bool check(ll x)//除以每个2^i就可以得出2^i有多少个,这样子加完就得到对应的a[x]是多少。
{
    ll num=0;
    for(int i=0;i<=63;i++)
        num+=x/p[i];
    return num>=n-1;
}
ll solve(ll x)
{
    ll num=0;
    for(int i=0;i<=63;i++)
        num+=x/p[i];
    return num;
}
int t;
int main()
{
    dabiao();
    ll inv2=qmod(2ll,mod-2);
    p[0]=1;
    for(int i=1;i<=63;i++)
        p[i]=p[i-1]*2;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld",&n);
        ll l=max(n/2-30,1ll),r=min(n,n/2+30),pos;
        while(l<=r)//二分找n对应的a[n],pos记录下a[n]
        {
            ll mid=(l+r)>>1;
            if(check(mid))
            {
                pos=mid;
                r=mid-1;
            }
            else l=mid+1;
        }
        ll s=pos-1,ans=1;//pos-1是因为在a[n]位置的n可能不是刚好符合2^i,所以要找前面一个,前面一个必定是符合2^i个的
        for(int i=0;i<=63;i++)
        if(s>=p[i])
        {
            ll a1=1,an=s/p[i]%mod;
            (ans+=p[i]*(a1+an)%mod*an%mod*inv2%mod)%=mod;//等差数列求和
        }
        (ans+=(n-1-solve(s))%mod*pos%mod)%=mod;///处理最后多出来的那些不参与上面等差数列的项
        printf("%lld\n",ans);
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值