Xorequ 數位dp+矩陣快速冪
題目描述
給定正整數n, 現有如下方程:x^2x=3x , 其中^表示按位異或。
任務如下:
- 求出小於等於n的正整數中,有多少個數是該方程的解
- 求出小於等於2n的正整數中,有多少數是方程的解,模109+7
輸入格式
第一行一個正整數,表示資料組資料T,接下來T行
每行一個正整數N
輸出格式
輸出2*T行
第2*i-1行表示第i個數據中問題一的解
第2*i行表示第i個數據中問題二的解
樣例
樣例輸入
1
1
樣例輸出
1
2
樣例解釋
x==1與x==2 都是原方程的根,注意第一個問題的解不要mod109+7
資料範圍與提示
1<=N<=1018,1<=T<=1000
問題一應該算是數位dp比較簡單的題了
先來推一下性質x^2x=3x,按位異或的話要優先考慮二進位制的轉移:
2x就是x向左移一位,x^2x如果二進位制中有重複位為1的話,那麼異或得到的數只會比3x更小,3x可看作x+2x的不進位加法(其實這也是異或本身的性質)
所以x的尋找就是要找到小於等於n的正整數中二進位制位沒有連續1的數,方案數加和
開的陣列真的好小qwq
問題二:2的1e18次方的話肯定就不能按位枚舉了,因為要列舉(1e18-1)位,就算是O(n)的複雜度也不夠,所以要考慮O(logn)的
在問題一中之所以要按位列舉,是因為給的數轉移成二進位制每一位都會存在限制,而問題二的2的幾次方的二進位制就是10000……
減一的話,由n位變成了(n-1)位,但是每一位都變成了1,111111……
從最高位開始就不會存在限制,就算每一位都選1也可以。
再來看一下如何求解,每一位上的數是0或1,手畫一下可以發現一個遞推的關係:
(i表示位數,cnt表示當前滿足條件的方案數)
i==1時,cnt=2(1,0
i==2時,cnt=3(10,01,00
i==3時,cnt=5(101,100,010,001,000
i==4時,cnt=8(1010,1001,1000,0101,0100,0010,0001,0000
i==5時,cnt=13(10101,10100,10010,10001,10000,01010,01001,01000,00101,00100,00010,00001,00000
所以,這是一個菲波那契數列,用矩陣快速冪O(logn
還有一個小細節,就是,由n位變成了(n-1)位時,減1之前是100000……,因為只有一個1,所以這個數一定是滿足條件的,方案數可以++
但是要求的x是正整數,所以應該減去沒有任何1但不是正整數的0,方案數--
所以求問題二的時候,可以抵消,但是dfs求方案一的時候就需要減去0的那個一種方案
1 #include <bits/stdc++.h> 2 #define mod 1000000007 3 using namespace std; 4 typedef long long ll; 5 int T,a[62]; 6 ll n,dp[62][2]; 7 ll dfs(int cnt,int flag,int k) { 8 if(!cnt) return 1; 9 if(!flag&&~dp[cnt][k]) return dp[cnt][k]; 10 ll res=0; 11 int Max=flag?a[cnt]:1; 12 for(int i=0;i<=Max;i++) { 13 if(i) { 14 if(!k) res+=dfs(cnt-1,flag&&(i==Max),1); 15 } 16 else res+=dfs(cnt-1,flag&&(i==Max),0); 17 } 18 if(!flag) return dp[cnt][k]=res; 19 return res; 20 } 21 ll query(ll x) { 22 int cnt=0; 23 ll res=0; 24 memset(dp,-1,sizeof(dp)); 25 memset(a,0,sizeof(a)); 26 while(x) { 27 a[++cnt]=x&1; 28 x>>=1; 29 } 30 for(int i=0;i<=a[cnt];i++) { 31 res+=dfs(cnt-1,(i==a[cnt]),(i==1)); 32 } 33 return res; 34 } 35 struct Matrix{ 36 ll mat[2][2]; 37 Matrix() {} 38 Matrix operator*(Matrix const &b) const { 39 Matrix res; 40 memset(res.mat,0,sizeof(res.mat)); 41 for(int i=0;i<2;i++) { 42 for(int j=0;j<2;j++) { 43 for(int k=0;k<2;k++) { 44 res.mat[i][j]=(res.mat[i][j]+mat[i][k]*b.mat[k][j])%mod; 45 } 46 } 47 } 48 return res; 49 } 50 }; 51 Matrix pow_mod(Matrix base,ll n) { 52 Matrix res; 53 memset(res.mat,0,sizeof(res.mat)); 54 for(int i=0;i<2;i++) res.mat[i][i]=1; 55 while(n) { 56 if(n&1) res=res*base; 57 base=base*base; 58 n>>=1; 59 } 60 return res; 61 } 62 int main() { 63 scanf("%d",&T); 64 Matrix base; 65 for(int i=0;i<2;i++) { 66 for(int j=0;j<2;j++) { 67 base.mat[i][j]=1; 68 } 69 } 70 base.mat[1][1]=0; 71 while(T--) { 72 scanf("%lld",&n); 73 printf("%lld\n",query(n)-1); 74 Matrix ans=pow_mod(base,n+2); 75 printf("%lld\n",ans.mat[0][1]); 76 } 77 return 0; 78 }