题意:有一个长度为n的序列,让你把它分成k段,段内元素取or,段间取and,求能够得到的最大值。
这个算法是我和xz场上yy出来的,然而时间不够了没写出来,而且时间复杂度是$O(nlogn+nlogA)$的比官方题解都要低...(但是常数大了点)
设最大值为ans,我们假设S&ans=S,看看S能否用k条线段凑出来,则将原问题转化成了一个判定问题。从高到低一位一位地考虑,最多只需进行$O(logA)$次判定。
如何进行判定呢?
首先将原数组复制一倍接到后面,然后进行两次尺取。第一次求出每个左端点l所对应的能够覆盖S的最小的右端点r并把它作为一条线段放进数组里(能够覆盖S的意思是S的每一位上的1都可以在[l,r]区间里的某个元素中取到,可以用RMQ预处理区间or然后$O(1)$判断),第二次则对这些线段进行尺取,求出每条线段右边第一条和它不相交的线段,将每条线段与这样的线段连边,可以得到一棵树(或者森林,若为森林则将所有树和一个虚节点连边即可变成一棵树),只需要检查一下这棵树上是否有一个结点的l和与它距离为k的父亲结点的r的区间长度r-l+1是否小于n,从根节点dfs一遍即可。
代码:(我写了两份,第一份怕爆栈所以手写了数组模拟栈,第二份是普通的dfs)
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=1e6+10; 5 int n,k,a[N],ST[N][20],Log[N],nl,hd[N],ne,q[N],tp; 6 void build() { 7 for(int i=1; i<=2*n; ++i)ST[i][0]=a[i]; 8 for(int k=1; k<=Log[2*n]; ++k) 9 for(int i=1; i+(1<<k)-1<=2*n; ++i) 10 ST[i][k]=ST[i][k-1]|ST[i+(1<<(k-1))][k-1]; 11 } 12 int qry(int L,int R) { 13 int k=Log[R-L+1]; 14 return ST[L][k]|ST[R-(1<<k)+1][k]; 15 } 16 struct D {int l,r;} line[N]; 17 struct E {int v,nxt;} e[N]; 18 struct ND {int u,dep;} sta[N]; 19 void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;} 20 bool dfs() { 21 sta[tp=0]= {nl,0}; 22 while(~tp) { 23 int u=sta[tp].u,dep=sta[tp--].dep; 24 q[dep]=u; 25 if(dep>=k&&line[q[dep-k+1]].r-line[u].l+1<=n)return 1; 26 for(int i=hd[u]; ~i; i=e[i].nxt)sta[++tp]= {e[i].v,dep+1}; 27 } 28 return 0; 29 } 30 bool ok(int S) { 31 nl=0; 32 for(int i=1,j=1; i<=2*n; ++i) { 33 if(j<i)j=i; 34 for(; j<=2*n&&(qry(i,j)&S)!=S; ++j); 35 if(j<=2*n)line[nl++]= {i,j}; 36 } 37 for(int i=0; i<=nl; ++i)hd[i]=-1; 38 ne=0; 39 for(int i=0,j=0; i<nl; ++i) { 40 for(; j<nl&&line[j].l<=line[i].r; ++j); 41 addedge(j,i); 42 } 43 return dfs(); 44 } 45 int solve() { 46 int ret=0; 47 for(int i=30; i>=0; --i)if(ok(ret|(1<<i)))ret|=1<<i; 48 return ret; 49 } 50 int main() { 51 Log[0]=-1; 52 for(int i=1; i<N; ++i)Log[i]=Log[i>>1]+1; 53 scanf("%d%d",&n,&k); 54 for(int i=1; i<=n; ++i)scanf("%d",&a[i]); 55 for(int i=1; i<=n; ++i)a[i+n]=a[i]; 56 build(); 57 printf("%d ",solve()); 58 return 0; 59 }
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=1e6+10; 5 int n,k,a[N],ST[N][20],Log[N],nl,hd[N],ne,q[N]; 6 void build() { 7 for(int i=1; i<=2*n; ++i)ST[i][0]=a[i]; 8 for(int k=1; k<=Log[2*n]; ++k) 9 for(int i=1; i+(1<<k)-1<=2*n; ++i) 10 ST[i][k]=ST[i][k-1]|ST[i+(1<<(k-1))][k-1]; 11 } 12 int qry(int L,int R) { 13 int k=Log[R-L+1]; 14 return ST[L][k]|ST[R-(1<<k)+1][k]; 15 } 16 struct D {int l,r;} line[N]; 17 struct E {int v,nxt;} e[N]; 18 void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;} 19 bool dfs(int u,int dep) { 20 q[dep]=u; 21 if(dep>=k&&line[q[dep-k+1]].r-line[u].l+1<=n)return 1; 22 for(int i=hd[u]; ~i; i=e[i].nxt)if(dfs(e[i].v,dep+1))return 1; 23 return 0; 24 } 25 bool ok(int S) { 26 nl=0; 27 for(int i=1,j=1; i<=2*n; ++i) { 28 if(j<i)j=i; 29 for(; j<=2*n&&(qry(i,j)&S)!=S; ++j); 30 if(j<=2*n)line[nl++]= {i,j}; 31 } 32 for(int i=0; i<=nl; ++i)hd[i]=-1; 33 ne=0; 34 for(int i=0,j=0; i<nl; ++i) { 35 for(; j<nl&&line[j].l<=line[i].r; ++j); 36 addedge(j,i); 37 } 38 return dfs(nl,0); 39 } 40 int solve() { 41 int ret=0; 42 for(int i=30; i>=0; --i)if(ok(ret|(1<<i)))ret|=1<<i; 43 return ret; 44 } 45 int main() { 46 Log[0]=-1; 47 for(int i=1; i<N; ++i)Log[i]=Log[i>>1]+1; 48 scanf("%d%d",&n,&k); 49 for(int i=1; i<=n; ++i)scanf("%d",&a[i]); 50 for(int i=1; i<=n; ++i)a[i+n]=a[i]; 51 build(); 52 printf("%d ",solve()); 53 return 0; 54 }