您的位置:首页 > 其它

Codeforces 875D High Cry [枚举+二进制]

2017-10-24 15:46 489 查看
题意:给你n个数,求区间满足区间或大于等于区间最大值。

题解:对于区间个数的问题,一般可能涉及到二分、尺取、或者枚举等。这题首先,我们考虑所有区间的个数n*(n+1)/2,我们可以通过减去不符合条件的区间来计算符合条件的区间,然后枚举区间最大值,对于当前这个数,我们找右边小于等于他的最右边界和左边小于他的最左边界(右等左不等,防止相同数的时候重复计算)。我们可以通过set将数字从大到小加入,然后找到这个区间。

对于这个数二进制每一位,假如要对区间值产生影响的话,那必须是最大值的数这一位上为0,其他数当前位置存在1,不符合条件的情况是左右最近的位置上的1的位置。

最后将二进制每一位得到的不符合条件的区间求交,最后区间(I-L+1)*(R-i+1)就是不符合条件的区间个数。

AC代码:

#include<stdio.h>
#include<algorithm>
#include<set>
using namespace std;
typedef long long ll;
ll a[200005];
ll s[200005];
ll pre[200005][35];
ll last[200005][35];
ll L[200005],R[200005];
set<ll>st;
set<ll>::iterator it;
bool cmp(ll A,ll B)
{
if(a[A]==a[B])return A<B;
return a[A]>a[B];
}
int main()
{
ll n;
scanf("%lld",&n);
for(ll i=1;i<=n;i++)
{
scanf("%lld",&a[i]);s[i]=i;
ll now=a[i];
for(ll j=0;j<=32;j++)
{
if(now%2==1)pre[i][j]=i;
else pre[i][j]=pre[i-1][j];
now/=2;
}
}
for(ll i=0;i<=32;i++)
last[n+1][i]=n+1;
for(ll i=n;i>=1;i--)
{
ll now=a[i];
for(ll j=0;j<=32;j++)
{
if(now%2==1)last[i][j]=i;
else last[i][j]=last[i+1][j];
now/=2;
}
}
sort(s+1,s+n+1,cmp);
st.insert(0);
st.insert(n+1);
ll ans=n*(n+1)/2;
for(ll i=1;i<=n;i++)
{
it=st.insert(s[i]).first;
it--;
L[s[i]]=*it;
it++;it++;
R[s[i]]=*it;
ll l=L[s[i]],r=R[s[i]];
if(a[l]==a[s[i]])L[s[i]]=L[l],R[l]=min(R[l],s[i]);
}
for(ll i=1;i<=n;i++)
{
ll ansl=L[i]+1,ansr=R[i]-1;
for(ll j=0;j<=32;j++)
{
if(pre[i][j]==i)continue;
ansl=max(ansl,pre[i][j]+1);
}
for(ll j=0;j<=32;j++)
{
if(last[i][j]==i)continue;
ansr=min(ansr,last[i][j]-1);
}
ans-=(i-ansl+1)*(ansr-i+1);
}
printf("%lld\n",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: