Codeforces 875D High Cry [列舉+二進位制]
阿新 • • 發佈:2019-02-19
題意:給你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); }