• BZOJ 3329 Xorequ:数位dp + 矩阵快速幂


    传送门

    题意

    现有如下方程:$ x oplus 3x = 2x $

    其中 $ oplus $ 表示按位异或。

    共 $ T $ 组数据,每组数据给定正整数 $ n $,任务如下:

    1. 求出小于等于 $ n $ 的正整数中,有多少个数是该方程的解
    2. 求出小于等于 $ 2^n $ 的正整数中,有多少个数是该方程的解,输出 $ mod $ $ 10^9+7 $ 的值。

    $ (n leq 10^{18}, T leq 1000) $

    题解

    第一问

    方程 $ x oplus 3x = 2x $ 等价于 $ x oplus 2x = 3x $ 。

    由于 $ x + 2x = 3x $ ,并且“按位异或”相当于“不进位加法“

    所以我们可以知道,一个数 $ x $ 是该方程的解的充要条件为:$ x $ 在二进制表示下,没有两个相邻的 $ 1 $ 。

    $ f[i][0/1] $ 表示所有二进制下长度为 $ i $,且最右边一位为 $ 0/1 $ 的数中,满足没有两个相邻的 $ 1 $ 的数的个数。

    所以有:

    [f[i][0] = f[i-1][0] + f[i-1][1] ]

    [f[i][1] = f[i-1][0] ]

    边界条件为:$ f[0][0] = 1 $

    然后按位统计,算出小于等于 $ n $ 的答案:

    • 先让 $ n = n + 1 $ (因为最后算出的是小于某个数的答案,而这里要求小于等于)
    • 从高到低枚举 $ n $ 的每一个二进制位 $ a[i] $
    • 如果 $ a[i] = 1 $ ,那么 $ ans = ans + f[i][0] $ 。(此时累加的是第 $ i-1 $ 及之前位和原数匹配时的答案)
    • 如果 $ a[i] = 1 $ 且 $ a[i+1] = 1 $ ,退出循环。因为此时的前缀已经有两个 $ 1 $ 相邻,之后的答案都是不合法的。

    注意最终答案为 $ ans - 1 $ ,因为不包含 $ 0 $ 这个答案。

    复杂度 $ O(logn) $

    第二问

    $ g[i] $ 表示二进制下长度为 $ i $ ,满足没有两个相邻的 $ 1 $ 的数的个数。

    由于题目要求区间 $ [1, 2^i] $ 的答案,$ g[i] $ 表示区间 $ [0, 2^i-1] $ 的答案

    而 $ 0 $ 和 $ 2^i $ 一定都满足条件,所以相互抵消掉了。

    所以题目所求 $ ans = g[n] $

    然后考虑如何求 $ g[i] $ (分两种情况):

    • $ g[i] $ 中以 $ 0 $ 结尾的所有数,相当于在 $ g[i-1] $ 中的所有数末尾添了一个 $ 0 $
    • $ g[i] $ 中以 $ 1 $ 结尾的所有数,相当于在 $ g[i-2] $ 中的所有数末尾添了一个 $ 01 $

    所以有:

    [g[i] = g[i-1] + g[i-2] ]

    边界条件为:$ g[1] = 2, g[2] = 3 $

    然后用矩阵快速幂加速转移即可:

    [egin{bmatrix} g[i] & g[i+1] end{bmatrix} imes egin{bmatrix} 0 & 1 \ 1 & 1 end{bmatrix} = egin{bmatrix} g[i+1] & g[i+2] end{bmatrix} ]

    [egin{bmatrix} g[1] & g[2] end{bmatrix} imes egin{bmatrix} 0 & 1 \ 1 & 1 end{bmatrix}^{n-1} = egin{bmatrix} g[n] & g[n+1] end{bmatrix} ]

    AC Code

    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #define MAX_L 5
    #define MAX_B 70
    #define MOD 1000000007
    #define int long long
    
    using namespace std;
    
    struct Mat
    {
    	int n,m;
    	int v[MAX_L][MAX_L];
    	Mat(int _n,int _m) { n=_n,m=_m,memset(v,0,sizeof(v)); }
    	Mat() { memset(v,0,sizeof(v)); }
    };
    
    int n,t;
    int a[MAX_B];
    int dp[MAX_B][2];
    
    Mat get_unit(int x)
    {
    	Mat a(x,x);
    	for(int i=0;i<x;i++) a.v[i][i]=1;
    	return a;
    }
    
    Mat mul(const Mat &a,const Mat &b)
    {
    	Mat c(a.n,b.m);
    	for(int i=0;i<a.n;i++)
    	{
    		for(int j=0;j<b.m;j++)
    		{
    			for(int k=0;k<a.m;k++)
    			{
    				c.v[i][j]+=a.v[i][k]*b.v[k][j];
    				c.v[i][j]%=MOD;
    			}
    		}
    	}
    	return c;
    }
    
    Mat pow(Mat a,int k)
    {
    	Mat ans=get_unit(a.n);
    	while(k>0)
    	{
    		if(k&1) ans=mul(ans,a);
    		a=mul(a,a),k>>=1;
    	}
    	return ans;
    }
    
    int cal1()
    {
    	int t=n+1,len=0,ans=0;
    	while(t) a[++len]=(t&1),t>>=1;
    	a[len+1]=0,dp[0][0]=1;
    	for(int i=1;i<=len;i++)
    	{
    		dp[i][0]=dp[i-1][0]+dp[i-1][1];
    		dp[i][1]=dp[i-1][0];
    	}
    	for(int i=len;i>=1;i--)
    	{
    		if(a[i]) ans+=dp[i][0];
    		if(a[i+1] && a[i]) break;
    	}
    	return ans-1;
    }
    
    int cal2()
    {
    	Mat a(1,2),b(2,2);
    	a.v[0][0]=2,a.v[0][1]=3;
    	b.v[0][1]=b.v[1][0]=b.v[1][1]=1;
    	return mul(a,pow(b,n-1)).v[0][0];
    }
    
    signed main()
    {
    	scanf("%lld",&t);
    	while(t--)
    	{
    		scanf("%lld",&n);
    		printf("%lld
    ",cal1());
    		printf("%lld
    ",cal2());
    	}
    }
    
  • 相关阅读:
    poj 2243 bfs 利用 结构体中的step成员保存步数 ,STL的队列
    poj 1915 双向 BFS 利用数组 a[x][y] = a[cp.x][cp.y] + 1; b[x][y] = b[cp.x][cp.y] + 1;保留步数
    poj 1915 BFS 利用 pre 计算步数------------------路径
    hdu 1242
    poj 2243
    rwkj 1502
    png-CRC32校验
    uva-331-枚举-交换的方案数
    uva-301-枚举-组合
    关于docker
  • 原文地址:https://www.cnblogs.com/Leohh/p/9097474.html
Copyright © 2020-2023  润新知