Codeforces Round #631 (Div. 2)-D

思路

首先要明白b数组,其实就是a数组的异或前缀和,那么要怎么保证异或递增?选的数拆成二进制之后肯定需要位数递增才行,所以我们考虑把d拆成2进制,分成很多块儿,就是这样

[1],[10,11],[100,101,110,111],[1000,…],…

分成上面这样的块,然后从这里面找序列就可以了,然后发现这些块的大小是1 2 4 8 16这样的,然后问题就变成了从x个块中分别取1,2,3,4,5~x个数,然后得到的sum,发现这些数都是2的次幂对吧,我们就找它的系数。它的系数应该怎么找的?考虑当 2 k 2^k 2k为最高的位置时,例如 2 2 2^2 22在第三位的时候,前面可以有 [ 2 0 , 2 1 , 2 2 ] [2^0,2^1,2^2] [20,21,22]这样如果在第二位就有 [ 2 0 , 2 2 ] 与 [ 2 1 , 2 2 ] [2^0,2^2]与[2^1,2^2] [20,22][21,22],然后它的系数就是在它前面的和。接下来这样还是不太好求它的系数,但是我们发现如果从低到高的求系数的话,就可以使用前面的信息,例如当我求出 2 1 2^1 21的系数为2,也就是 [ 2 0 , 2 1 ] 以 及 [ 2 1 ] [2^0,2^1]以及[2^1] [20,21][21]本身,还有 2 0 2^0 20的系数为1,也就是[2^0]自身,那么 2 2 2^2 22的系数就相当于在它前面出现过的序列相加啊,也就是 [ 2 0 , 2 1 ] , [ 2 1 ] , [ 2 0 ] [2^0,2^1],[2^1],[2^0] [20,21],[21],[20]的后面加上 [ 2 2 ] [2^2] [22],也就变成 [ 2 0 , 2 1 , 2 2 ] , [ 2 1 , 2 2 ] , [ 2 0 , 2 2 ] [2^0,2^1,2^2],[2^1,2^2],[2^0,2^2] [20,21,22],[21,22],[20,22]所以我们可以直接递增的求出系数6来。当然它可能不一定是整块,最后剩余的部分怎么考虑?前面有x-1块,处理完后,我们就在这些序列的后面直接加上yu的话,也就是多了这些序列*yu的序列,然后还有后面一部分自成序列的情况,也就是再+yu即可。

代码实现

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
const int N=35;
int fac[N];
inline bool isprime(ll num)
{if(num==2||num==3)return true;
if(num%6!=1&&num%6!=5)return false;
for(int i=5;1ll*i*i<=num;i+=6){if(num%i==0||num%(i+2)==0)return false;}
return true;}
const int mod = 1e9+7;
inline ll mul(ll a,ll b,ll c){return (a*b-(ll)((ld)a*b/c)*c+c)%c;}
inline ll exgcd(ll a,ll b,ll &x,ll &y){if(!b){x=1;y=0;return a;}ll g = exgcd(b,a%b,y,x);y-=a/b*x;return g;}
inline ll quick_pow(ll a,ll b,ll mod){ll res=1;while(b){if(b&1)res=mul(res,a,mod);a=mul(a,a,mod);b>>=1;}return res;}
inline ll quick_pow(ll a,ll b){ll res=1;while(b){if(b&1)res=mul(res,a,mod);a=mul(a,a,mod);b>>=1;}return res;}
inline ll inv(ll x){return quick_pow(x,mod-2);}
inline ll inv(ll x,ll mod){return quick_pow(x,mod-2,mod);}
inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
int main(){
    fac[0]=1;
    int d,m;
    int t;
    scanf("%d",&t);
    while(t--){
	scanf("%d %d",&d,&m);
	for(int i=1;i<=32;i++)fac[i]=1ll*2*fac[i-1]%m;
	int lim = 0;
	ll ans = 0;
	while((1ll<<lim)<=d+1)lim++;
	ll yu = d-(1ll<<(lim-1))+1;
	ll add=1;
	for(int i=1;i<lim;i++){
		ans+=1ll*add*fac[i-1];
		add+=1ll*fac[i-1]*add;
		add%=m;
		ans%=m;
	}
	ans=(ans*(yu+1)+m)%m;
	ans=(ans+yu+m)%m;
	printf("%lld\n",ans);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值