bzoj3329 Xorequ

http://www.lydsy.com/JudgeOnline/problem.php?id=3329

题意:已知方程x xor 3x=2x,给定N,求该方程有多少个不大于N的正整数解,有多少个不大于2^N的正整数解,第二问的答案对1000000007取模。N<=10^18。

方程等价于x xor 2x=3x,又有x+2x=3x,易知x and 2x=0。

再注意到2x实际上就是x<<1,容易发现该方程成立的充要条件是x的二进制表达没有2个连续的1。

注意到2^N和0都一定是方程的解,因此第二问可以等价于,求N位的01序列,有多少种满足没有2个连续的1。

考虑递推式,若首位是1,则第二位必然是0,第三位开始则又可以任意选择,若首位是0,则第二位开始仍旧可以任意选择。

设F[N]为答案,显然有F[N]=F[N-1]+F[N-2],这就是著名的斐波那契数列。求第N项取模的结果用矩阵乘法。

考虑第一问,这是一个数位DP。不妨举例N=22,将N+1=23表达为二进制:10111。

第一段:0~1111,这相当于是第二问的一种情况,贡献即为F[4]。

第二段:10000~10011,忽略前三位,这又相当于是第二问的一种情况,贡献即为F[2]。

第三段:10100~10101,同理,贡献即为F[1]。

第四段:10110~10111,由于已经出现了连续两位1,显然后面的数一定不是解,提前结束数位DP。

总贡献即为F[4]+F[2]+F[1]=13。注意到我们这里统计的是0~N的解的数量,一定要除去0,因此最终答案为12。

两问都顺利解决。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#define size 2
#define mod 1000000007
long long T,n,i,ans,m;
long long fib[70];
int w[70];
struct M{
    long long v[size][size];
    M(){
        memset(v,0,sizeof(v));
    }
    friend M operator*(M a,M b){
        M ans;
        for(int i=0;i<size;i++)
            for(int j=0;j<size;j++)
                for(int k=0;k<size;k++)
                    ans.v[i][j]=(ans.v[i][j]+a.v[i][k]*b.v[k][j])%mod;
        return ans;
    }
    friend M operator^(M a,long long b){
        M ans;
        for(int i=0;i<size;i++)
            ans.v[i][i]=1;
        for(long long i=b;i;i=i/2,a=a*a)
            if(i&1)ans=ans*a;
        return ans;
    }
}a,b;
int main(){
    fib[0]=0;fib[1]=1;for(i=2;i<=64;i++) fib[i]=fib[i-1]+fib[i-2];
    a.v[0][0]=a.v[0][1]=a.v[1][0]=1;a.v[1][1]=0;
    scanf("%d",&T);
    while(T--){
        scanf("%lld",&n);++n;
        b=a^n;
        m=0;
        while(n) w[++m]=n%2,n/=2;
        w[m+1]=ans=0;
        for(i=m;i;i--){
            if(w[i]) ans+=fib[i+1];
            if(w[i]&&w[i+1]) break;
        }
        printf("%lld\n%lld\n",ans-1,b.v[0][0]);
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值