牛客小白月赛98 E.and xor or

原题链接:E-and xor or

题意:

思路:对于满足条件的区间,第二个条件要求的是从l到r的一堆数的按位与和按位或异或的值大于等于2的k1次方,并且小于2的k2次方。对于上下界限,从二进制的角度考虑,设中间的运算的值为x,化为二进制,那么x的范围应该在[10...00(中间k1个0) , 11....1(k2个1)],那么如果x要满足这个范围,x的最高二进制位的位置就应该在[k1 , k2-1]。对于中间这些数的每个位置进行考虑,那么如果是全1,或者全0,那么最后的值就是0,其他情况就是1。那么可以确定的是,如果一段区间的某个二进制位变成了1,那么继续扩大右边界这个二进制位数会一直为1,但是如果暴力枚举需要n^2的复杂度,那么可以确定右边界,然后去顺序的记录上一个和当前位置不同的二进制数的位置,例如1,0,0,1,0,1。记录下来就应该是0,1,1,3,4,5.意思是上一个从当前位置到记录的位置这一段区间是不满足的,例如位置3记录的是1,那么意思是[2,3],这段区间的这个二进制位是不能变成1的,如果要变成1,那么就必须从2往前,例如[1,3],如果这个二进制位满足要求,那么以3位右端点的区间贡献就是记录的1-0=1,这样就可以记录当前二进制位可以变成1的所有区间,但是还需要排除比当前二进制位高的位置变成的1,那么就可以开一个数组来记录高位二进制以当前位置为右端点的区间的左端点。

//冷静,冷静,冷静
//调不出来就重构
#pragma GCC optimize(2)
#pragma GCC optimize("O3")
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<ll,ll> pii;
const int N=1e6+10,mod=998244353;
ll l[N],p[N],max1[N]; 
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	ll n,k1,k2;cin>>n>>k1>>k2;
	for(int i=1;i<=n;i++)cin>>p[i];
	if(k1>61)//因为整数二进制最高位不会超过61 
	{
		cout<<0;
		return 0;
	}
	k2=min(61ll,k2);
	ll ans=0;
	for(int i=61;i>=0;i--)
	{
		for(int j=1;j<=n;j++)
		{
			if(j==1||((p[j]&(1ll<<i))==(p[j-1]&(1ll<<i))))//如果二进制和之前的一样那么左端点也是一样的 
			{
				l[j]=l[j-1];
			}
			else 
			{
				l[j]=j-1;//否则就是前一个数 
			}
			if(i>=k1&&i<k2)
			{
				ans=ans+max(0ll,l[j]-max1[j]);//因为如果之前的二进制数已经是1了,那么就不能计算了,会重复 
			}
			max1[j]=max(max1[j],l[j]);
		}
	}
	cout<<ans;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值