P1077 长街行 官方题解
【部分分:测试点 1∼31\sim 31∼3】
- 这一档的序列很短。对一次询问 ,可以枚举区间内所有非空位置集合 。
- 对每个集合直接计算
若结果不为 ,就用 更新答案。
- 单次询问的时间复杂度为 ,总时间复杂度为 。
【部分分:测试点 4∼74\sim 74∼7】
-
上一档枚举了所有集合,瓶颈在于集合数量。考虑一支合法队伍为何能够通过长街。
-
必要性:若若干整数按位与不为 ,结果中至少有一个二进制位为 ,因此所有被选整数在这一位上都是 。
-
充分性:固定一个二进制位,把区间中这一位为 的整数全部选出,它们的按位与在这一位上仍为 。
-
所以,合法队伍等价于共享某个值为 的二进制位。答案就是各二进制位出现次数的最大值。
-
这一档只有一次询问。枚举二进制位,并扫描整个询问区间。
-
记 为区间中第 位为 的整数个数,那么答案为
- 时间复杂度为 ,空间复杂度为 ,其中 表示值域。
【部分分:测试点 8∼158\sim 158∼15】
- 询问增多以后,反复扫描区间会成为新的瓶颈。可以预先求出所有区间的答案。
- 枚举左端点 ,再从 开始向右移动右端点 。每加入一个数,就更新它含有的所有二进制位的出现次数。
- 根据上一档的结论,当前各二进制位出现次数的最大值就是区间 的答案。之后每次询问可以在 时间内完成。
- 时间复杂度为 ,空间复杂度为 。
【正解】
- 上一档仍然求出了 个区间的答案,但真正需要回答的区间只有 个。只保留每个二进制位的前缀信息即可。
- 记 为命题 的 Iverson 括号。对每个二进制位 建立前缀和
- 在询问 中,第 位出现的次数为
- 根据已经证明的等价条件,一次询问的答案为
- 预处理前缀和需要 的时间,每次询问只需枚举二进制位。
- 总时间复杂度为 ,空间复杂度为 。
【参考代码】
/*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;}