【JZOJ4121】【THUSC2015】异或问题(可持久化trie)

Problem

  给定长度为n的数列X={x1,x2,…,xn}和长度为m的数列Y={y1,y2,…,ym},令矩阵A中第i行第j列的值Aij=xi xor yj。给出p个询问,每次询问给定矩形区域i∈[u,d],j∈[l,r],找出第k大的Aij。

Hint

对于100%的数据,0<=Xi,Yj<2^31,
1<=u<=d<=n<=1000,
1<=l<=r<=m<=300000,
1<=k<=(d-u+1)*(r-l+1),
1<=p<=500

Solution

  我刚做这题的时候一脸懵逼,想了想主席树,但是发现要维护n*m个数。。。
  正确的打开方式是建trie。。。(尽管我也想到了逐位处理)
  不过这也不能怪我,因为我似乎没有做过这样的套路。。。
这里写图片描述
  瞄到“Xi,Yj<2^31”,把每个数转二进制,变成31个0/1。
  我们发现“n<=1000”“m<=300000”,n这么小,可以暴力搞;我们就对于数列Y建trie即可。对于每个点,我们都记录经过这个点的数的数量cnt,就像权值线段树一样。
  然而这题要建可持久化trie(原因之后再说),思想类似主席树,每个数都给它新建32个点(还有根呢);当然,如果你连主席树都不会,可以考虑戳一戳这里
  然后,询问的时候,我们可以暴力搞数列X,然而还需要利用可持久化trie来搞数列Y(要区间查询第k大,普通trie吃不消啊)。我们对于i∈[u,d],都记录一个L[i]、R[i]分别表示xi在第l-1棵trie、第r棵trie上走到了哪个点。我们先看看这些所有i往哪个儿子走会使当前位的xor值变为1,求一下所有这些儿子的cnt的和chk,如果chk≥k则表明第k大的当前位为1,我们应当将所有的i往这些儿子走;否则表明第k大的当前位为0,我们将k-=chk,然后将所有的i往另外的一个儿子走。
  时间复杂度: O ( n + m 32 + p n 32 )

Code

#include <cstdio>
#define P X[i][j]
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;

const int N=1001,M=300*N,S=32*M;
int i,n,m,x,X[N][31];

void turnbit(int x,int*a)
{
    int i;
    fo(i,0,30)a[i]=x&1,x>>=1;
}

void scan()
{
    scanf("%d%d",&n,&m);
    fo(i,1,n)scanf("%d",&x),turnbit(x,X[i]);
}

int y,a[31],deep,rt[M],tot;
struct node
{
    int cnt,son[2];
}t[S];
void modify(int&v)
{
    int New=++tot;
    t[New]=t[v],v=New;
    t[v].cnt++;
    if(deep)modify(t[v].son[a[--deep]]);
}
void maketrie()
{
    fo(i,1,m)
    {
        scanf("%d",&y);
        turnbit(y,a);
        deep=31;
        modify(rt[i]=rt[i-1]);
    }
}

int p,u,d,l,r,k,L[N],R[N],ans;
void query()
{
    ans=0;
    int i,j,chk,x=1<<30;
    fo(i,u,d)L[i]=rt[l-1],R[i]=rt[r];
    fd(j,30,0)
    {
        chk=0;
        fo(i,u,d)chk+=t[t[R[i]].son[!P]].cnt-t[t[L[i]].son[!P]].cnt;
        if(chk>=k)
        {
            ans+=x;
            fo(i,u,d)L[i]=t[L[i]].son[!P],R[i]=t[R[i]].son[!P];
        }
        else
        {
            k-=chk;
            fo(i,u,d)L[i]=t[L[i]].son[ P],R[i]=t[R[i]].son[ P];
        }
        x>>=1;
    }
}
void work()
{
    for(scanf("%d",&p);p;p--)
    {
        scanf("%d%d%d%d%d",&u,&d,&l,&r,&k);
        query();
        printf("%d\n",ans);
    }
}

int main()
{
    scan();
    maketrie();
    work();
}

猜你喜欢

转载自blog.csdn.net/qq_36551189/article/details/80440820