1. 程式人生 > >CF875D High Cry

CF875D High Cry

include del -a span const class -i 計算 ger

傳送門

題目要求合法的區間個數,這裏考慮用總區間個數減去不合法的個數

假設某個數為區間最大值,那麽包含這個數的最長區間內,所有數小於他並且所有數沒有這個最大值沒有的二進制位,可以按位考慮每個數\(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;
}

CF875D High Cry