1. 程式人生 > 實用技巧 >Xorequ 數位dp+矩陣快速冪

Xorequ 數位dp+矩陣快速冪

題目描述

給定正整數n, 現有如下方程:x^2x=3x , 其中^表示按位異或。

任務如下:

  1. 求出小於等於n的正整數中,有多少個數是該方程的解
  2. 求出小於等於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 }