https://codeforces.com/contest/1330/problem/D
給出d,m, 找到一個a數組,滿足以下要求:
a數組的長度為n,n≥1;
1≤a1<a2<⋯<an≤d;
定義一個數組b:b1=a1, ∀i>1,bi=bi−1⊕ai ,並且 b1<b2<⋯<bn−1<bn ;
求滿足條件的a數組的個數並模m;
人話:求一個a數組滿足遞增,並且異或前綴和也遞增 ,求出a數組個數mod m。
太菜了,不會,看了很多題解才會的,這里總結一下:
參考官方題解 https://codeforces.com/blog/entry/75559
首先思考數組遞增並且前綴異或和也遞增(異或運算:不進位加法 1^1=0,1^0=1,0^0=0),那么就必須滿足后面一個數二進制的最高位1的位置大於前面一個數的二進制的最高位1的位置,我們來看下為什么,假如最高位和前一個相同,那么前綴異或和最高位的1就消掉了,肯定會變小,最高位比前一個低,那這個數就比前一個數小了,不滿足數組遞增,所以要比前一個數的最高位高。
每個數1的最高位 h(ai)=v, v代表ai的最高位,例如:h(1)=0,h(2)=h(3)=1, and h(4)=h(7)=2。
找出每個最高位的個數,例如 h(ai)=v,ai的最高位為v,找到最高位為v的區間,[2v,2(v+1)−1] ,還要注意一個邊界問題,不能大於d,所以為:[2v,min(2(v+1)−1,d)],那么個數就為 min(2(v+1)−1,d)-2v+1,最后再加上不選的一種情況,最后為min(2(v+1)−1,d)-2v+1+1。然后分組,把最高位相同的分在一起,每一次都從一個組里面選擇一個數或者不選,來組成a數組,就是把每個最高位的個數都乘起來(不選的情況我把它算進最高位的個數里面了),再減掉全部不選的情況,就好了。
例如:d=6,最高位為0的有1個,那么我們有兩種選擇,選1或者不選;最高位為1的有2個,那么我們有三種選擇,選2或者3或者不選,最高位為2的有3個,那么我們有四種選擇,選4或者5或者6或者不選;然后組合在一起,里面有全部不選的一種情況,所以最后結果要減1,即:2*3*4-1=23.
寫得很啰嗦,因為我看了好久才看懂 ~~
#include <bits/stdc++.h> using namespace std; const int MAXN=1e5+5; const int mod=1e9+7; typedef long long ll; //typedef __int128 LL; const int inf=0x3f3f3f3f; const long long INF=0x3f3f3f3f3f3f3f3f; int main() { int t; scanf("%d",&t); while(t--) { ll d,m; scanf("%lld%lld",&d,&m); ll ans=1; for(int i=0;i<=32;i++) { if(d<(1<<i))break; ans=(ans*(min(d,(1ll<<(i+1))-1)-(1<<i)+1+1))%m; } ans--; if(ans<0)ans+=m; printf("%lld\n",ans); } return 0; }
還有一種遞推的方法,參考博客:https://www.cnblogs.com/AaronChang/p/12635428.html
用一個cnt[i]數組來記錄每個最高位的個數,每次選擇了一個數之后就從后面的數中選擇;
dp[i]表示二進制的最高位1在前i位(包含第i位)的方案數之和,dp[i]=dp[i-1]+dp[i-1]*cnt[i]+cnt[i] ,(只單獨取cnt[i]也可以)
#include <bits/stdc++.h> using namespace std; const int MAXN=1e5+5; const int mod=1e9+7; typedef long long ll; //typedef __int128 LL; const int inf=0x3f3f3f3f; const long long INF=0x3f3f3f3f3f3f3f3f; ll dp[40],cnt[40]; int main() { int t; scanf("%d",&t); while(t--) { ll d,m; scanf("%lld%lld",&d,&m); ll a=d,idx=0,t=1; while(a) { cnt[++idx]=min(d-(t-1),t);//下標從1開始,方便dp數組 t<<=1; a>>=1; } for(int i=1;i<=idx;i++)dp[i]=((dp[i-1]+(dp[i-1]*cnt[i])%m)%m+cnt[i])%m; printf("%lld\n",dp[idx]); } return 0; }
