2018.09.30【LOJ517】「LibreOJ β Round #2」計算幾何瞎暴力(01Trie)(二進位制拆分)
阿新 • • 發佈:2019-01-01
傳送門
解析:
看到標題的先不要急著錘我。。。
這道題的二進位制拆分和不能混在一起,不要急著說就是二進位制拆分。。。
思路:
這道題可以說是非常好的一道資料結構。
我相信應該沒有人會去想計算幾何。(那這出題人得有多善良)
先看操作1,要求在末尾插入一個數,這個與資料結構沒有什麼關係,我們可以直接在資料結構外建一個數組存。
操作2,詢問區間和,這個就是維護字首和,還是不能幫助我們確定使用什麼資料結構。
操作3,全域性。
空氣凝固。。。這個除了還能怎麼做?
操作4,全域性排序。。。內部的數字本來就是排了序的。我們只需要記錄之前了一個什麼東西就能夠確定內部具體的順序,在每次排序的時候將相數組裡面的數全部插入樹裡面。
那這道題就是咕咕咕了。
好,最(被和諧)的地方到了。
對於一個有全域性的題,我們要怎麼維護字首和?
這裡就是剛才提到的二進位制拆分的作用了。
對於所有的我們都不直接作用在陣列和樹上,而是用一個標記記錄當前應該要上一個什麼東西。
而字首和,我們將所有數的二進位制表示拆開,分位統計字首和(統計該位出現次數)。記錄在數組裡面。那麼表示前位中在第位出現了多少個,那麼出現的的個數就是。這兩個個數都是在異或上之前而言的
每次詢問一個位置,我們就看一下這一位是否是1,而採取是統計還是。
同理,在樹中,我們統計每個節點子樹出現各個位的次數。在記錄字首和的時候根據子樹大小調整就行了。
不要忘了在葉子節點的時候,只能統計該統計的部分,這個需要單獨處理一下。
假裝被和諧掉了的複雜度分析
根據我們剛才的敘述,我們可以得到一個不太對的複雜度。
單次的複雜度
樹單次插入的複雜度(插入+二進位制字首和)
樹單次詢問的複雜度
陣列單次插入
陣列單次詢問複雜度
單次排序複雜度。
而總複雜度可以近似認為是
程式碼:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline
ll getint(){
re ll num;
re char c;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
return num;
}
inline
void outint(ll a){
static char ch[23];
if(0==a)pc('0');
while(a)ch[++ch[0]]=a-a/10*10,a/=10;
while(ch[0])pc(ch[ch[0]--]^48);
}
cs int N=100005;
int xortag;
struct _01TRIE{
#define root 0
int son[N*30][2];
int siz[N*30];
int sum[N*30][30];
int tot;
int tag;
_01TRIE(){tot=0;}
void insert(int a){
int now=root;
for(int re i=29;~i;--i){
bool f=a&(1<<i);
if(!son[now][f])son[now][f]=++tot;
now=son[now][f];
++siz[now];
for(int re j=29;~j;--j){
if(a&(1<<j))++sum[now][j];
}
}
}
ll querysubtree(int pos){
ll ans=0;
for(int re i=29;~i;--i)
if(xortag&(1<<i))ans+=(1ll*siz[pos]-sum[pos][i])<<i;
else ans+=sum[pos][i]*1ll<<i;
return ans;
}
ll querysum(int x){
if(x==0)return 0;
ll ans=0;
int now=root;
for(int re i=29;~i;--i){
bool f=0;
if(tag&(1<<i))f^=1;
if(x<=siz[son[now][f]])now=son[now][f];
else{
ans+=querysubtree(son[now][f]);
x-=siz[son[now][f]];
now=son[now][!f];
}
}
ans+=querysubtree(now)/siz[now]*x;
return ans;
}
int size(){
return siz[son[root][0]]+siz[son[root][1]];
}
#undef root
}Trie;
struct array{
int tot;
int sum[N][30];
int h[N];
void insert(int a){
h[++tot]=(a^=xortag);
for(int re i=29;~i;--i)
sum[tot][i]=sum[tot-1][i]+((a>>i)&1);
}
ll querysum(int pos){
ll ans=0;
for(int re i=29;~i;--i)
if(xortag&(1<<i))ans+=(1ll*pos-sum[pos][i])<<i;
else ans+=sum[pos][i]*1ll<<i;
return ans;
}
void sort(){
while(tot)Trie.insert(h[tot--]);
Trie.tag=xortag;
}
}Array;
inline
ll query(int pos){
if(pos>Trie.size())return Trie.querysum(Trie.size())+Array.querysum(pos-Trie.size());
return Trie.querysum(pos);
}
int n,m;
signed main(){
n=getint();
for(int re i=1;i<=n;++i){
int a=getint();
Array.insert(a);
}
m=getint();
while(m--){
int op=getint();
switch(op){
case 1:{
int x=getint();
Array.insert(x);
break;
}
case 2:{
int l=getint(),r=getint();
outint(query(r)-query(l-1));pc('\n');
break;
}
case 3:{
xortag^=getint();
break;
}
case 4:{
Array.sort();
break;
}
}
}
return 0;
}