← 返回题解列表

P1077 长街行 官方题解

【部分分:测试点 1∼31\sim 31∼3】

  • 这一档的序列很短。对一次询问 [l,r][l,r],可以枚举区间内所有非空位置集合 SS
  • 对每个集合直接计算
iSai,\bigwedge_{i\in S}a_i,

若结果不为 00,就用 S\lvert S\rvert 更新答案。

  • 单次询问的时间复杂度为 O(2nn)\mathcal O(2^n n),总时间复杂度为 O(q2nn)\mathcal O(q2^n n)

【部分分:测试点 4∼74\sim 74∼7】

  • 上一档枚举了所有集合,瓶颈在于集合数量。考虑一支合法队伍为何能够通过长街。

  • 必要性:若若干整数按位与不为 00,结果中至少有一个二进制位为 11,因此所有被选整数在这一位上都是 11

  • 充分性:固定一个二进制位,把区间中这一位为 11 的整数全部选出,它们的按位与在这一位上仍为 11

  • 所以,合法队伍等价于共享某个值为 11 的二进制位。答案就是各二进制位出现次数的最大值。

  • 这一档只有一次询问。枚举二进制位,并扫描整个询问区间。

  • cjc_j 为区间中第 jj 位为 11 的整数个数,那么答案为

max0j30cj.\max_{0\leq j\leq 30}c_j.
  • 时间复杂度为 O(nlogV)\mathcal O(n\log V),空间复杂度为 O(logV)\mathcal O(\log V),其中 VV 表示值域。

【部分分:测试点 8∼158\sim 158∼15】

  • 询问增多以后,反复扫描区间会成为新的瓶颈。可以预先求出所有区间的答案。
  • 枚举左端点 ll,再从 ll 开始向右移动右端点 rr。每加入一个数,就更新它含有的所有二进制位的出现次数。
  • 根据上一档的结论,当前各二进制位出现次数的最大值就是区间 [l,r][l,r] 的答案。之后每次询问可以在 O(1)\mathcal O(1) 时间内完成。
  • 时间复杂度为 O(n2logV+q)\mathcal O(n^2\log V+q),空间复杂度为 O(n2)\mathcal O(n^2)

【正解】

  • 上一档仍然求出了 O(n2)\mathcal O(n^2) 个区间的答案,但真正需要回答的区间只有 qq 个。只保留每个二进制位的前缀信息即可。
  • [P][P] 为命题 PP 的 Iverson 括号。对每个二进制位 jj 建立前缀和
sj,i=k=1i[ak 的第 j 位为 1].s_{j,i}=\sum_{k=1}^{i}[a_k\text{ 的第 }j\text{ 位为 }1].
  • 在询问 [l,r][l,r] 中,第 jj 位出现的次数为
sj,rsj,l1.s_{j,r}-s_{j,l-1}.
  • 根据已经证明的等价条件,一次询问的答案为
max0j30(sj,rsj,l1).\max_{0\leq j\leq 30}\left(s_{j,r}-s_{j,l-1}\right).
  • 预处理前缀和需要 O(nlogV)\mathcal O(n\log V) 的时间,每次询问只需枚举二进制位。
  • 总时间复杂度为 O((n+q)logV)\mathcal O((n+q)\log V),空间复杂度为 O(nlogV)\mathcal O(n\log V)

【参考代码】

/*Author:EhundateghDate:2026/8/31Name:street.cppYou steal,I kill.*/#include <cstdio>#include <cstring>#include <algorithm>#define MAXN 200010using namespace std; int c,T,n,q,Line[MAXN],Pre[31][MAXN]; void Solve(){    scanf("%d%d",&n,&q);    for(int i=1;i<=n;i++) scanf("%d",&Line[i]);    for(int j=0;j<=30;j++){        Pre[j][0]=0;        for(int i=1;i<=n;i++) Pre[j][i]=Pre[j][i-1]+((Line[i]>>j)&1);    }    while(q-->0){        int l,r,Ans=0;        scanf("%d%d",&l,&r);        for(int j=0;j<=30;j++) Ans=max(Ans,Pre[j][r]-Pre[j][l-1]);        printf("%d\n",Ans);    }    return;} int main(){    freopen("street.in","r",stdin);    freopen("street.out","w",stdout);    scanf("%d%d",&c,&T);    while(T-->0) Solve();    return 0;}