1. 程式人生 > >Codeforces Round #441 F題題解

Codeforces Round #441 F題題解

答案 long hid 一個數 src else 單調棧 pri b2c

技術分享

技術分享

題目大意

給出一個有n個數的序列

求符合 區間各數或起來的數小於區間最大數 的區間的個數

題解

預處理出每個數每一位是0的那位左邊最近的1和右邊最近的1,用單調棧找出每個最大值所在的區間的左右端點,統計答案即可。

技術分享
#include<cstdio>
#include<algorithm>
#include<cstring> 
#define LL long long
using namespace std;
const int maxn=500010,inf=2e9;
int n,top,st[maxn],a[maxn],digit[maxn][32],pre[maxn][32
],Pre[maxn],next[maxn][32],Next[maxn],cnt[maxn]; LL ans; void read(int &k){ k=0; int f=1; char c=getchar(); while (c<0||c>9)c==-&&(f=-1),c=getchar(); while (0<=c&&c<=9)k=k*10+c-0,c=getchar(); k*=f; } int main(){ read(n); for (int i=1;i<=n;i++){ read(a[i]);
for (int x=a[i];x;x>>=1) digit[i][++cnt[i]]=x&1; //處理出a[i]二進制下的每一位 } //////////////////////////////////////////// //pre[i][j]表示:在第j位上,第i個數為0時,左邊最近的為1的位置;next[i][j]為右邊最近的1的位置 for (int j=1;j<=30;j++){ int last=0; for (int i=1;i<=n;i++) if (!digit[i][j]) pre[i][j]=last; else last=i; }
for (int j=1;j<=30;j++){ int first=n+1; for (int i=n;i;i--) if (!digit[i][j]) next[i][j]=first; else first=i; } //////////////////////////////////////////// //對於一個數,不合法區間的左端點為其各個為0數位上,左邊最近的1的位置的最大值 //右端點為其各個為0數位上,右邊最近的1的位置的最小值 //即對於maxnumber,它的每個為0位,不合法區間內的其他數的這一位都為0,這樣區間or起來之後等於maxnumber memset(Next,32,sizeof(Next)); for (int i=1;i<=n;i++) for (int j=1;j<=30;j++) if (!digit[i][j]) Pre[i]=max(Pre[i],pre[i][j]),Next[i]=min(Next[i],next[i][j]); //////////////////////////////////////////// 單調棧維護以a[i]為最大值的區間的左右端點 a[++n]=inf; for (int i=1;i<=n;i++){ for (;top&&a[i]>=a[st[top]];top--){ ans+=1LL*((i-1)-st[top]+1)*(st[top]-(st[top-1]+1)+1); //以a[st[top]]為最大值的全部區間個數 ans-=1LL*(st[top]-max(st[top-1]+1,Pre[st[top]]+1)+1)*(min(i-1,Next[st[top]]-1)-st[top]+1); //減去不合法的區間個數 } st[++top]=i; } printf("%lld\n",ans); return 0; }
View Code

Codeforces Round #441 F題題解