题目大意
给你一个数组a,定义f(i,j)=ai|ai+1|ai+2|⋯|aj ,|为or运算,求满足f(i,j)<m的二元组个数,N≤105,m≤230
题解
枚举起点i,然后找出最靠右的k,使得f[i,k)<m,计算f(i,k)的值可以用线段树来维护,查询只需要logn时间,求k这个位置可以用二分法求出,总的时间复杂度为nlogn^2,刚好能够趟过。。。话说有很多人是用O(n^2)暴力每一对f(i,j),然后加上剪枝,速度奇快。。。。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; #define MAXN 100005 #define lson l,m,s<<1 #define rson m+1,r,s<<1|1 int sumv[MAXN<<2],a[MAXN]; void PushUp(int s) { sumv[s]=sumv[s<<1]|sumv[s<<1|1]; } void build(int l,int r,int s) { if(l==r) { sumv[s]=a[l]; return; } int m=(l+r)>>1; build(lson); build(rson); PushUp(s); } int query(int ql,int qr,int l,int r,int s) { if(ql<=l&&r<=qr) return sumv[s]; int m=(l+r)>>1; int ans=0; if(ql<=m) ans=ans|query(ql,qr,lson); if(qr>m) ans=ans|query(ql,qr,rson); return ans; } int main() { int T; scanf("%d",&T); for(int t=1;t<=T;t++) { int n,m; long long ans=0; scanf("%d%d",&n,&m); for(int i=0;i<n;i++) scanf("%d",&a[i]); build(0,n-1,1); for(int i=0;i<n;i++) { if(a[i]>=m) continue; int l=i,r=n-1; while(l<=r) { int mid=(l+r)>>1; int sum=query(i,mid,0,n-1,1); if(sum>=m) r=mid-1; else l=mid+1; } ans+=r-i+1; } printf("Case #%d: %I64d ",t,ans); } return 0; }