背景:博主前天晚上水了一波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;
}