• 【BZOJ3329】Xorequ(数位dp+矩阵快速幂)


    x ⊕ 3 x = 2 x xoplus3x=2x x3x=2x 推出 x ⊕ 2 x = 3 x xoplus2x=3x x2x=3x,然后又有 x + 2 x = 3 x x+2x=3x x+2x=3x

    定理:若 a ⊕ b = c aoplus b=c ab=c a + b = c a+b=c a+b=c,则不可能存在 a a a b b b 在二进制下的某一位都是 1 1 1

    证明:设 a a a 在二进制下的第 i i i 位为 a i a_i ai b b b 在二进制下的第 i i i 位为 b i b_i bi c c c 在二进制下的第 i i i 位为 c i c_i ci。显然有 a 1 + b 1 = c 1 a_1+b_1=c_1 a1+b1=c1 a 1 ⊕ b 1 = c 1 a_1oplus b_1=c_1 a1b1=c1,因为这一位不可能得到进位。分类讨论一下发现 a 1 a_1 a1 b 1 b_1 b1 不可能都是 1 1 1,那么第一位就不可能向第二位进位。第二位也就能用同样的方法证出来 a 2 a_2 a2 b 2 b_2 b2。以此类推, a a a b b b 二进制下的任何一位都不能全是 1 1 1

    那么我们就可以知道 x x x 2 x 2x 2x 二进制下的任何一位都不能全是 1 1 1

    又因为 2 x 2x 2x 是由 x x x 左移一位得到的,所以就等价为:二进制下的 x x x 任意相邻的两位都不能是 1 1 1

    那么对于第一问,把 x x x 转成二进制数,然后数位dp。

    对于第二问:

    f i , 0 / 1 f_{i,0/1} fi,0/1 代表满足以下条件的二进制数(可以有前导0)的个数: i i i 位数,第一位为 0 / 1 0/1 0/1 ,没有连续的两位为 1 1 1

    那么显然第二问的答案就是 f n = f n , 0 + f n , 1 f_n=f_{n,0}+f_{n,1} fn=fn,0+fn,1

    考虑 f i f_i fi 的状态转移:

    f i , 0 = f i − 1 , 0 + f i − 1 , 1 = f i − 1 f_{i,0}=f_{i-1,0}+f_{i-1,1}=f_{i-1} fi,0=fi1,0+fi1,1=fi1

    f i , 1 = f i − 1 , 0 f_{i,1}=f_{i-1,0} fi,1=fi1,0

    f i = f i − 1 + f i − 1 , 0 = f i − 1 + f i − 2 f_i=f_{i-1}+f_{i-1,0}=f_{i-1}+f_{i-2} fi=fi1+fi1,0=fi1+fi2

    那么这就是一个斐波那契数列,用矩阵快速幂求一下就好了。

    代码如下:

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mod 1000000007
    
    using namespace std;
    
    struct Matrix
    {
    	ll a[2][2];
    	Matrix(){memset(a,0,sizeof(a));}
    	void init(){a[0][0]=a[1][1]=1;}
    	Matrix operator * (const Matrix &b) 
    	{
    		Matrix c;
    		for(int i=0;i<2;i++)
    			for(int j=0;j<2;j++)
    				for(int k=0;k<2;k++) 
    					c.a[i][j]=(c.a[i][j]+a[i][k]*b.a[k][j])%mod;
    		return c;
    	}
    }ch;
    
    int t;
    int cnt,num[100];
    ll n,dp[100][2][2];
    
    void init()
    {
    	ch.a[1][0]=ch.a[0][1]=ch.a[1][1]=1;
    }
    
    ll dfs(int k,bool limit,bool last)
    {
    	if(!k) return 1;
    	if(!dp[k][limit][last])
    	{
    		dp[k][limit][last]+=dfs(k-1,limit&&!num[k],0);
    		if((!last)&&((!limit)||(limit&&num[k]))) dp[k][limit][last]+=dfs(k-1,limit&&num[k],1);
    	}
    	return dp[k][limit][last];
    }
    
    ll solve1()
    {
    	cnt=0;
    	ll x=n;
    	while(x)
    	{
    		num[++cnt]=x&1ll;
    		x>>=1ll;
    	}
    	return dfs(cnt,1,0)-1;
    }
    
    ll solve2()
    {
    	Matrix a=ch,ans;
    	ans.init();
    	ll b=n+2;
    	while(b)
    	{
    		if(b&1ll) ans=ans*a;
    		a=a*a;
    		b>>=1ll;
    	}
    	return ans.a[0][1];
    }
    
    int main()
    {
    	init();
    	scanf("%d",&t);
    	while(t--)
    	{
    		memset(dp,0,sizeof(dp));
    		scanf("%lld",&n);
    		printf("%lld
    ",solve1());
    		printf("%lld
    ",solve2());
    	}
    	return 0;
    }
    
  • 相关阅读:
    POJ1182 食物链---(经典种类并查集)
    HDU1025---(LIS 最长上升子序列 的应用)
    HDU5748---(记录每个元素的 最长上升子序列 nlogn)
    c++ 批量初始化数组 fill和fill_n函数的应用
    JNI 方法注册与签名+BufferedReader使用readLine问题
    正确在遍历中删除List元素
    Head First Java设计模式思维导图总结
    关于一些基础的Java问题的解答(七)
    关于一些基础的Java问题的解答(六)
    关于一些基础的Java问题的解答(四)
  • 原文地址:https://www.cnblogs.com/ez-lcw/p/14448669.html
Copyright © 2020-2023  润新知