Codeforces 631 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;
}

还有一种递推的方法,参考博客: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;
}

原文地址:https://www.cnblogs.com/MZRONG/p/12652432.html

时间: 2024-10-10 14:36:16

Codeforces 631 D. Dreamoon Likes Sequences 位运算^ 组合数 递推的相关文章

[位运算] [搜索] [递推优化] [计算几何] TEST 2016.7.15

NOIP2014 提高组模拟试题 第一试试题 题目概况: 中文题目名称 合理种植 排队 科技节 源程序文件名 plant.pas/.c/.cpp lineup.pas/.c/.cpp scifest.pas/.c/.cpp 输入文件名 plant.in lineup.in scifest.in 输出文件名 plant.out lineup.out scifest.out 每个测试点时限 1s 1s 1s 测试点数目 10 10 10 每个测试点分值 10 10 10 内存上限 128MB 128

Codeforces Round #631 (Div. 2) D.Dreamoon Likes Sequences

题目连接:Dreamoon Likes Sequences  题意:给你d和m,让你构造一个递增数组a,使数组b(i==1,b[i]=a[i] ; i>1, b[i]=b[i-1]^a[i])递增,求a有几种,答案模m. 题解:根据异或的性质可以得出:2后边不能有3, 4后边不能有5~7, 8后边不能有9~15...... 然后就很好写了.用数组b记录第i个数可以取得数有多少个,数组dp记录长度为 i 的 a 数组有几种.看下边的代码应该就清楚了. 1 #include<bits/stdc++

Codeforces 558C Amr and Chemistry(数论+位运算)

C. Amr and Chemistry time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output Amr loves Chemistry, and specially doing experiments. He is preparing for a new interesting experiment. Amr has n differ

LeetCode 187. Repeated DNA Sequences(位运算,hash)

题目 题意:判断一个DNA序列中,长度为10的子序列,重复次数超过1次的序列! 题解:用一个map 就能搞定了,但是出于时间效率的优化,我们可以用位运算和数组代替map,首先只有四个字母,就可以用00,01,10,11 四个二进制表示,长度为10的序列,可以用长度为20的二进制序列表示.这样每中组合都对应一个数字,然后用数组表示每个数字出现的次数就好了. class Solution { public: int m[1<<21]; int m3[1<<21]; int m2[127

Codeforces Round #631 (Div. 2) Dreamoon Likes Sequences

题面很短,别的博客也讲了就不说题意了. 做法: 异或是没有进位的加法,所以ai + 1的二进制最高位要大于ai的二进制最高位,才能满足ai递增,bi也递增的条件.呐这样的话,选了4,(5,6,7)就都不能选了,只能选比7大的数. 这样分析下来a数组最长也只有30,(2^30>1e9) 直接按照数字大小dp会TLE 思路角度1:换一个角度,我们把二进制最高位相同的看作一组,因为这一组内只能选一个数. 有点像分组背包.但是我们现在只看分组背包的方案数,所以就不用枚举每一组内的物品了. dpij表示考

codeforces #320 div 2A - Raising Bacteria (位运算)

A - Raising Bacteria Time Limit:1000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Status Description You are a lover of bacteria. You want to raise some bacteria in a box. Initially, the box is empty. Each morning, you can put

CodeForces 558C Amr and Chemistry (位运算,数论,规律,枚举)

Codeforces 558C 题意:给n个数字,对每个数字可以进行两种操作:num*2与num/2(向下取整),求:让n个数相等最少需要操作多少次. 分析: 计算每个数的二进制公共前缀. 枚举法亦可. /* *Author : Flint_x *Created Time : 2015-07-22 12:33:11 *File name : whust2_L.cpp */ #include<iostream> #include<sstream> #include<fstrea

Codeforces Round #455 (Div. 2) C. Python Indentation dp递推

Codeforces Round #455 (Div. 2) C. Python Indentation 题意:python 里面,给出 n 个 for 循环或陈述语句,'f' 里面必须要有语句.按 python 缩进的方式组合成合法的程序,问有多少种可能方案. tags: dp dp[i][j] 表示第 i 个语句缩进为 j 时的可能方案数, 转移: 1] 如果第 i 个是 'f' , 则第 i+1 个肯定要比第 i 个多缩进一个单位,即 dp[i+1][j] = dp[i][j]. 2]如果

codeforces 630C - Lucky Numbers 递推思路

630C - Lucky Numbers 题目大意: 给定数字位数,且这个数字只能由7和8组成,问有多少种组合的可能性 思路: 假设为1位,只有7和8:两位的时候,除了77,78,87,88之外还哇哦加上前面只有7和8的情况,一共是6位.所以递推式不难写出dp[i]=pow(2,i)+dp[i-1]; 代码: #include <bits/stdc++.h> using namespace std; typedef long long ll; ll ans[56]; void init ()