「LibreOJ β Round #2 D题」计算几何瞎暴力 Trie树整体xor的trick

题目链接

背景:博主前天晚上水了一波LOJ的BetaRound2,花了2h写了某三个水水的签到题后便处于无所事事的颓废状态,

            想rush一下D题,但idea一直是挂的,最终3题大众分#26滚粗.

            看了题解之后,顿时感觉D题的做法非常棒,同时深深的感受到了博主的菜鸡本质……

            好了,那么不说废话,直接讲讲这道题的解法吧.

题解:一个序列,要求支持4种操作——–>那么这一定就是个大数据结构

           那么分别来看这4种操作:1,2分别是添加元素和区间求和,都很常规,暂时先不管;

                                                          操作3是整个序列Xor,这种与位运算相关的东西,Trie树?

                                                          操作4是整个序列排序,看上去有点奇怪.

           博主十分菜鸡,想到排序后的序列整体Xor后很有可能再次无序,便没有什么好的办法了,只能弃疗。

           沿着操作3Trie树的idea继续往下想,

           Trie树可以支持整棵树打Xor标记,

           只要在Trie树上遍历时判断这一位是否参加了Xor运算,

           如果参加了,遍历时交换Trie树的两个儿子即可,这样操作3可以O(1)实现;

           对于操作4,可以考虑分别维护一个有序序列和无序序列,

           这样,对于操作2的询问,可以拆分为Query(R)-Query(L-1)转化为对于前缀和的询问,

           如果询问的节点落在无序序列中,直接维护前缀和即可,

           如果询问的节点落在有序序列中,对于有序序列维护Trie树,可以在Trie树上二分解决,

           此时每个Trie树节点需要维护logAi个值,表示这个点为根的子树中每个位出现次数.

           无序序列中,因为存在Xor标记,所以按位维护前缀和即可,O(logN);

           有序序列中,相当于Trie树的插入操作,也可以O(logN)完成,

           但是由于每个Trie节点维护的logAi个值需要更新,时间复杂度是O(logNlogAi)的.

           每次1操作,将元素插入无序序列,

           而4操作时再将所有没有出现在有序序列中的数插入有序序列即可,

           这里需要注意的是,Trie树的有序是对于最近一次排序操作时的Xor标记而言的,记为pXor,在Trie树上二分是根据pXor做的,

                                               而每次2操作在Trie树上统计答案是对于现在的Xor标记做的,两者需要加以区分.

           这样,就可以O(nlognlogAi)的时间和空间解决此题了,

          (UPD:其实我觉得空间大概满了,接近1G,但是实际运行时好像只用了256M左右,还是LOJ机子妙)

Code:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline int read()
{int x=0;
char c=getchar();
while (c<'0'||c>'9') c=getchar();
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x;
}
struct trie
{int sz,a[31],ch[2];
}t[6000005];
int qz[200005][31],n,m,rt=1,cnt=1;
int lxor=0,pxo=0,las=0,tp=0,sq[200005];
inline void add(int num)
{num^=lxor;sq[++tp]=num;
for (int i=0;i<=29;i++)
{qz[tp][i]=qz[tp-1][i]+((num>>i)&1);}
}
inline ll q(int pos)
{int i,j;
ll ans=0;
if (pos>las)
{for (i=0;i<=29;i++)
{if (lxor&(1<<i))
{ans+=((ll)(pos-qz[pos][i]))<<((ll)i);}
else {ans+=((ll)qz[pos][i])<<((ll)i);}
}
return ans;
}
int pl=rt;
for (i=29;i>=0;i--)
{int c1=t[pl].ch[0],c2=t[pl].ch[1];
if (pxo&(1<<i)) 
{int t=c1;c1=c2;c2=t;}
if (t[c1].sz<=pos)
{for (j=29;j>=0;j--)
{if (lxor&(1<<j))
{ans+=((ll)(t[c1].sz-t[c1].a[j]))<<((ll)j);}
else {ans+=((ll)(t[c1].a[j]))<<((ll)j);}
}
pl=c2;pos-=t[c1].sz;
}
else
{pl=c1;}
}
int t1,t2;
for (j=29;j>=0;j--)
{if (lxor&(1<<j)) {t1=1;}
else {t1=0;}
if (t[pl].a[j]) {t2=1;}
else {t2=0;}
t1^=t2;
if (t1) {ans+=((ll)pos)<<((ll)j);}
}
return ans;
}

inline void ins(int num)
{int pos=rt,i,j;
for (i=29;i>=0;i--)
{int lp=(num>>i)&1;
if (!t[pos].ch[lp]) {t[pos].ch[lp]=++cnt;}
t[pos].sz++;
for (j=29;j>=0;j--) 
{t[pos].a[j]+=((num>>j)&1);}
pos=t[pos].ch[lp];
}
t[pos].sz++;
for (j=29;j>=0;j--) 
{t[pos].a[j]+=((num>>j)&1);}
return;
}
inline void release()
{while (las<tp)
{las++;ins(sq[las]);}
}
int main (){
	int i,x,opt,l,r;
	n=read();
	for (i=1;i<=n;i++)
	{x=read();add(x);}
	m=read();
	for (i=1;i<=m;i++)
	{opt=read();
	if (opt==1)
	{x=read();add(x);}
	if (opt==2)
	{l=read();r=read();
	printf ("%lld\n",q(r)-q(l-1));
	}
	if (opt==3) {x=read();lxor^=x;}
	if (opt==4) {pxo=lxor;release();}
	}
	return 0;
}	

    原文作者:Trie树
    原文地址: https://blog.csdn.net/Marco_L_T/article/details/74364217
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞