P7906 [Ynoi2005] rpxleqxq 题解
P7906 [Ynoi2005] rpxleqxq 题解
题目大意#
给定一个长度为 的序列 ,和一个常数 。
有 次询问,每次给定一个区间 ,询问有多少二元组 ,满足:
- ;
- 。
Solve#
前置知识:莫队二次离线。
对于普通莫队,端点移动时,例如右端点从 移动到 ,对答案的贡献为 内和 的异或和小于等于 的元素 的个数。对于这个的维护,一种想法是,枚举 是在哪一位上和 决出来谁小,这样就确定了合法 二进制表示下的一个前缀,剩下的位随便填。可以使用 01 trie
维护前缀出现次数,做到插入和查询复杂度都是 。
然后考虑二离,维护 为 左边和它异或和小于等于 的元素个数。可以沿用 01 trie
,复杂度是 。
理一下思路。二离之后,我们有 次插入和 次查询。使用 01 trie
的话,可以做到插入和查询复杂度都是 。所以我们考虑平衡一下插入和查询的复杂度,使得查询做到 。
可以在插入 时还是枚举决胜位 ,那么对于一个能对其产生贡献的 ,它的 位都是确定的,其中 。而 位可以任意填,即从 ,是一个区间加操作。也就是说,我们需要在值域上维护一个数据结构,支持区间加和单点查,要求单点查复杂度是 。分块维护即可。这样的话,若块长 ,我们就得到了一个 的算法。理论约 。
写完之后手搓极限数据,本地只跑了 秒。交上去发现获得了谷的最优解。
复杂度还是不够优秀,理论不是很对。调整一下块长。考虑我插入一个数时,在值域上修改的若干区间都是不交的,所以对于整块的修改复杂度最坏就是 的,而对于散块,修改复杂度是 ;单次总复杂度是 ,当 时取得最小值,为 。
这样,算法整体复杂度就来到了 。又交了一发,快了一点。
Code#
const int N=2e5+10,B=200,T=105,V=2e5;
int w[N],sum[N],p[N],l[N],r[N];
inline void modify(const int &x,const int &y)//给 x ~ y 区间加一
{
if(x>y) return;
int u=p[x],v=p[y];
if(u==v)
{
for(int i=x;i<=y;i=-~i) w[i]=-~w[i];
return;
}
for(int i=x;i<=r[u];i=-~i) w[i]=-~w[i];
for(int i=-~u;i<v;i=-~i) sum[i]=-~sum[i];
for(int i=l[v];i<=y;i=-~i) w[i]=-~w[i];
}
inline void get_block()
{
int R=V/T+(V%T!=0);
for(int i=1;i<=R;i=-~i)
{
l[i]=-~r[i-1];r[i]=i==R?V:r[i-1]+T;
for(int j=l[i];j<=r[i];j=-~j) p[j]=i;
}
}
inline void insert(const int &x)
{
for(int i=17,p=0,a,b,now=0;~i;i=~-i)
{
a=x>>i&1;b=k>>i&1;
if(b) modify(now|(a<<i),min(now|(a<<i)|(1<<i)-1,V));
now|=(a^b)<<i;
}
if((x^k)<=V) w[x^k]=-~w[x^k];
}
inline int query(const int &x){return w[x]+sum[p[x]];}
剩下的就是莫二离板子。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】