专栏文章
Solution P11955 「ZHQOI R1」覆盖
P11955题解参与者 3已保存评论 2
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 2 条
- 当前快照
- 1 份
- 快照标识符
- @mipue43x
- 此快照首次捕获于
- 2025/12/03 18:06 3 个月前
- 此快照最后确认于
- 2025/12/03 18:06 3 个月前
这个题很人类智慧啊!
首先考虑 的情况。我们可以看成从 层的线段树添加 个叶子,然后让叶子尽可能被之前的区间进行拓展然后选到。

如上图。由于某个区间不能包含三个相邻的同层节点(否则就会合并成一个大的),因此 层的所有节点最优情况下都可以连向一条到第 层的边,即最优情况下有 个点已经被连好了。
考虑上图的这种构造方式,首先每个 层的节点都对应一个 层的节点,且剩下的节点除了头尾之外两两配对,配对的两个可以只使用一次区间覆盖。于是发现这是一种较为优秀的构造方式。
设 表示长度为 时的答案(这里并没有考虑 的区间,因此答案还要加 ),则有 ,边界为 。容易做到 。
解决了特殊性质 AB 后,考虑拓展这个做法。例如从 个点到 个点,过程中每次都相当于添加了一个叶子。如果叶子位置是那些已经被覆盖的点就不用管,否则如果在上图黄色的区间内,则需要额外操作。发现加入的前 个叶子都需要额外的一次操作覆盖,而后面的叶子无需操作,因此区间 (记为 )的答案即为 ,等差数列求和即可。
这是完全包含的情况,不完全包含的情况(即要求 的前缀答案和, 夹在 中间)同理。答案变成 。也可以快速求出。
场上写的 可过。精细实现即可做到 。
CPPconst int P=353442899;
int pw[80];
il int count(int x){
if(!x)return 0;
int ans=x;
forto(i,1,61){
int l=1ll<<i,r=(1ll<<(i+1))-1;if(l>x)break;
if(r<=x){
if(i>1){
ans+=(__int128)pw[i]%P*(r-l+1)%P,ans%=P;
int d=1ll<<(i-1);
ans+=(__int128)(r-l+r-l-d+1)*d/2%P,ans%=P;
}else if(i==1)ans+=5;
}else{
if(i>1){
// cerr<<l<<' '<<x<<' '<<pw[i]<<'\n';
ans+=(__int128)pw[i]%P*(x-l+1)%P,ans%=P;
int d=min(1ll<<(i-1),x-l);//cerr<<d<<'\n';
ans+=(__int128)(x-l+x-l-d+1)*d/2%P,ans%=P;
}else if(i==1)ans+=2;
}
}
return ans;
}
il void work(){
int l=read(),r=read();
printf("%lld\n",(count(r)-count(l-1)+P)%P);
}
signed main(){
pw[0]=0,pw[1]=2;
forto(i,2,60)pw[i]=(pw[i-1]+(1ll<<(i-2))+1)%P;
// forto(i,1,60)cerr<<pw[i]<<'\n';
int t=read();while(t--)work();
return 0;
}
相关推荐
评论
共 2 条评论,欢迎与作者交流。
正在加载评论...