Codeforces 1330D. Dreamoon Likes Sequences(思維 + 遞推) /詳解

D. Dreamoon Likes Sequences

題意:
給你d和m,讓你構造一個遞增數組a,使數組b(i==1,b[i]=a[i] ; i>1, b[i]=b[i-1]^a[i])遞增,求a有幾種,答案模m。

思路:

  1. 這題是比賽時沒寫出來的,也沒搞懂,今天逛了一圈,也沒發現什麼人寫的比較詳細,所以就打算寫一下這篇博客,算是讓自己梳理一下思路。
  2. 首先,因爲b數列的生成是b[i-1] xor a[i] ,所以如果a[i-1]在第i-1位選了1的話,那麼a[i]在第i位必須選1,即b[i]必須是遞增的,例如,a[i-1]=2,那麼a[i]必須是4,如果是3的話,那麼b[i]=b[i-1]^a[i],就會把第i位的1給消去,那麼此時b數列就不是遞增的了。
  3. 所以考慮取二進制第i位的話:有全0~全1的範圍 即2i+12^{(i+1)}-1種方案,要減去會消掉當前第i-1位1的方案即 2i2^{i}-1 ;
  4. 而考慮不取這第i位的話,就跳到下一位,但這也是一種方法,比如說給你的d是9: 假設每個位都取得話可以是:1 2 4 8 但是其中得某一位並不是一定要取的,例如1 2 8也是一種答案,所以不取的方案也要算到這一二進制位的貢獻上去,所以總的貢獻即:
  5. 2i+12^{(i+1)}-1 )-( 2i2^{i}-1 )+1
  6. 可化簡爲:2i+12^{(i+1)} - 2i2^{i} +1
  7. 最後注意的一點是當枚舉的二進制位趨向於d時,會出現能取的範圍並沒有 0~全1,這個時候就要取min( d ,2i+12^{(i+1)}-1)。
  8. 所以枚舉每個二進制位,把方案數累積起來,最後減去全0的方案,就是最後求的答案。
  9. 如果覺得有幫助的話給個贊,謝謝!
  10. 或者有什麼講的不清楚的歡迎留言私信交流~

代碼:

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define inf 0x3f3f3f3f
#define mod 1000000007
#define pb push_back
#define lowbit(x) (x&(-x))
using namespace std;
inline ll read(){ll s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*w;}
void put1(){ puts("Yes") ;}void put2(){ puts("No") ;}void put3(){ puts("-1"); }
ll qp(ll a,ll b, ll p){ll ans = 1;while(b){if(b&1){ans = (ans*a)%p;--b;}a =
(a*a)%p;b >>= 1;}return ans%p;}
const ull base=2333; const ull pp=19260811; const ull ppp=999998639;

const int manx=5e5+5;
const int mo=998244353;

ll dp[manx];

int main(){
    ll p=read();
    while(p--){
        ll d=read(),m=read();
        ll ans=1;
        for(int i=0;i<30;i++){
            ll x= (1<<i) ;
            if(d < x) break;
            ans  = ans * (min((x<<1)-1ll,d) - x +1 +1) % m;
        }
        ans = (ans -1 +m) %m;
        printf("%lld\n",ans);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章