湖北省省赛I题: Avengers: Infinite War(主席树+01字典树)


题目链接:点击打开链接


题目大意:有n个数a1~an m次查询 每次查询给出 val l r ,l r 询问val异或al~ar之间最大的值是多少。


解题思路:

怎么说呢,算是一道很典型的主席树模板题吧,但是自己太水了,因为之前没怎么做过01字典树的题目,所以对01字典树的认识就只停留在一个模板,也不知道是怎么实现的。所以当时比赛没想到把主席树和01字典树结合起来,所以比赛的时候采用了01字典树加莫队的做法,不过当时一直wa,有个bug没调出来,后来发现是因为01字典树是不能进行先删后加的操作的,这也是用模板的坏处吧,什么都不知道,不过最后改了以后还是t了,当时傻了,因为字典树的每次更新都是O(30),莫队想实现的话其实最主要就是更新答案的操作需要是O(1) ,所以最后复杂度n*30*根号n 就炸了,最后只过了85的数据,不过令我很费解的是我写的纯暴力代码过了95的数据,我真是惊了。。。


接下来说正解,正解其实就是主席树加01字典树的结合,如果两个知识点都明白的话应该是很容易理解做法的,每次更新都判断在每一层应该让0处数值+1 还是1处数值+1 最后维护一个主席树,查询的时候利用主席树的性质直接进行区间之间的查询+01字典树的判断即可。


Ac代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const ll INF=1e18+7;
const int mod=998244353;
int n,m,cnt,x,root[maxn];
struct node //主席树结点
{
    int l,r,sum;
}t[maxn*40];    
void update(int &x,int y,int k,int dep)
{
    t[++cnt]=t[y],t[cnt].sum++,x=cnt;
    if(dep<0) return ;
    if((k>>dep)&1) update(t[x].l,t[y].l,k,dep-1);   //判断当前层是1还是0 更新相应的位置
    else update(t[x].r,t[y].r,k,dep-1); 
}
int query(int x,int y,int k,int dep)    //查询
{
    if(dep<0) return 0;
    if((k>>dep)&1)  //01字典树性质 如果当前为1 就拿0和它异或
    {
        if(t[t[y].r].sum-t[t[x].r].sum>0) return (1<<dep)+query(t[x].r,t[y].r,k,dep-1); //如果l r之间的数中有此位为0的数 则ans+1<<dep
        else return query(t[x].l,t[y].l,k,dep-1);
    }
    else 
    {
        if(t[t[y].l].sum-t[t[x].l].sum>0) return (1<<dep)+query(t[x].l,t[y].l,k,dep-1);
        else return query(t[x].r,t[y].r,k,dep-1);
    }
}
int main()
{
    cnt=0; 
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&x),update(root[i],root[i-1],x,30);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        int val,l,r;
        scanf("%d%d%d",&val,&l,&r);
        printf("%d\n",query(root[l-1],root[r],val,30));
    }
    system("pause");
}  


猜你喜欢

转载自blog.csdn.net/f2935552941/article/details/80100775
今日推荐