Codeforces 631 (Div. 2) D. Dreamoon Likes Sequences 位運算^ 組合數 遞推


 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;
}
View Code

還有一種遞推的方法,參考博客: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;
}
View Code

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM