zoukankan      html  css  js  c++  java
  • 【洛谷5795】[THUSC2015] 异或运算(可持久化Trie)

    点此看题面

    大致题意: 给定一个长度为(n)的序列(x_{1sim n})和一个长度为(m)的序列(y_{1sim m}),矩阵(A)中第(i)行第(j)列的元素(A_{i,j}=x_i xor y_j),每次询问矩阵中满足行号(i∈[u,d]),列号(j∈[l,r])的子矩阵中第(k)大的元素。

    两个(log)的做法

    首先我们可以有一个复杂度较劣的两个(log)的做法:二分+可持久化(Trie)

    考虑对于序列(y)建一棵可持久化(Trie)

    然后,我们二分答案(mid)

    接着,枚举每一行(注意行数很少),求出对于每一行大于等于(mid)的数的个数。

    把这些个数加起来,如果大于等于(k),说明答案(mid)合法,并继续二分更大的答案,否则二分较小的答案。

    这个两个(log)的做法应该还是比较简单的。

    优化做法

    (XZY)神仙的指导下,我知道了该怎么把这个做法优化为一个(log)

    我们可以让询问的这些行一起在可持久化(Trie)上跑,每一行分别记下当前的对应的节点。

    然后从高到低枚举每一位,统计所有行对于这一位所能有的(1)的个数。

    如果这个个数大于等于(k),就说明这一位可以为(1),否则,这一位为(0)

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 1000
    #define M 300000
    using namespace std;
    int n,m,a[N+5],b[M+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define tn (x<<3)+(x<<1)
    		#define D isdigit(c=tc())
    		char c,*A,*B,FI[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    }F;
    class ChairmanTrie
    {
    	private:
    		int T;struct node {int Sz;node* S[2];I node() {Sz=0,S[0]=S[1]=NULL;}}*Rt[M+5],*l[N+5],*r[N+5];
    		#define Sz(x) (x==NULL?0:x->Sz)
    		#define S(x,d) (x==NULL?NULL:x->S[d])
    		I void Ins(node*& rt,node* lst,CI x,CI d)//插入
    		{
    			rt=new node,rt->Sz=Sz(lst),rt->S[0]=S(lst,0),rt->S[1]=S(lst,1);
    			if(++rt->Sz,!~d) return;int t=(x>>d)&1;Ins(rt->S[t],S(lst,t),x,d-1);
    		}
    		I int Qry(CI s,CI t,CI k,CI d)//询问
    		{
    			if(!~d) return 0;RI i,tot=0;
    			for(i=s;i<=t;++i) tot+=Sz(S(r[i],(a[i]>>d)&1^1))-Sz(S(l[i],(a[i]>>d)&1^1));//统计能有的1的个数
    			if(tot>=k)//如果个数大于等于k,说明这一位能填1
    			{
    				for(i=s;i<=t;++i) l[i]=S(l[i],(a[i]>>d)&1^1),r[i]=S(r[i],(a[i]>>d)&1^1);
    				return Qry(s,t,k,d-1)|(1<<d);
    			}
    			else//否则填0
    			{
    				for(i=s;i<=t;++i) l[i]=S(l[i],(a[i]>>d)&1),r[i]=S(r[i],(a[i]>>d)&1);
    				return Qry(s,t,k-tot,d-1);
    			}
    		}
    	public:
    		I ChairmanTrie() {for(RI i=1;i<=N;++i) Rt[i]=NULL;}
    		I void Ins(CI v,CI v_,CI x) {Ins(Rt[v],Rt[v_],x,30);}
    		I int Qry(CI vl,CI vr,CI s,CI t,CI k)
    		{
    			for(RI i=s;i<=t;++i) l[i]=Rt[vl-1],r[i]=Rt[vr];return Qry(s,t,k,30);//初始化每一行所对应的节点为根节点
    		}
    }T;
    int main()
    {
    	RI Qt,i,u,d,x,y,k,l,r,mid,t;F.read(n),F.read(m);
    	for(i=1;i<=n;++i) F.read(a[i]);for(i=1;i<=m;++i) F.read(b[i]),T.Ins(i,i-1,b[i]);//初始化可持久化Trie
    	F.read(Qt);W(Qt--) F.read(u,d,x,y,k),printf("%d
    ",T.Qry(x,y,u,d,k));return 0;//处理询问
    }
    
  • 相关阅读:
    validate BST
    LC282. Expression Add Operators
    nginx统计日志命令
    iptables和firewalld命令
    nginx安装
    测试服务器IO
    规范主机名和设置最大文件进程数
    Docker安装
    MySQL/MariaDB二进制安装
    Docker原理
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5795.html
Copyright © 2011-2022 走看看