CF875D High Cry

传送门

题目要求合法的区间个数,这里考虑用总区间个数减去不合法的个数

假设某个数为区间最大值,那么包含这个数的最长区间内,所有数小于他并且所有数没有这个最大值没有的二进制位,可以按位考虑每个数\(i\)\(j\)这一位上向左和向右第一个二进制位为1的位置,分别记为\(l_{i,j},r_{i,j}\),然后每个数再考虑所有二进制为0的位上的\(l_{i,j},r_{i,j}\)区间的交集,左右端点为\(ll,rr\),那个这一位对答案加上\((i-ll)*(rr-j)\)

要注意,前面可能有值相同的数,导致这次计算的\(ll\)包含了上次计算过的区间,这时候要让\(ll\)\(a_i\)上次出现的位置取max

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
#define il inline
#define re register
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define db double
#define eps (1e-8)

using namespace std;
const int N=200000+10;
il LL rd()
{
    re LL x=0,w=1;re char ch=0;
    while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
int n,a[N],l[N][33],r[N][33];
LL ans;
map<int,int> la;

int main()
{
  n=rd();
  for(int i=1;i<=n;i++) a[i]=rd();
  for(int i=1;i<=n;i++)
    for(int j=0;j<=32;j++)
      l[i][j]=(a[i]&(1<<j))?i:l[i-1][j];
  for(int j=0;j<=32;j++) r[n+1][j]=n+1;
  for(int i=n;i>=1;i--)
    for(int j=0;j<=32;j++)
      r[i][j]=(a[i]&(1<<j))?i:r[i+1][j];
  for(LL i=1;i<=n;i++)
    {
      LL ll=0,rr=n+1;
      for(int j=0;j<=32;j++)
        {
          if(a[i]&(1<<j)) continue;
          ll=max(ll,l[i][j]),rr=min(rr,r[i][j]);
        }
      ll=max(ll,la[a[i]]);
      la[a[i]]=i;
      ans+=(i-ll)*(rr-i);
    }
  printf("%lld\n",1ll*n*(n+1)/2-ans);
  return 0;
}

posted @ 2018-09-22 20:03  ✡smy✡  阅读(313)  评论(0编辑  收藏  举报