1. 程式人生 > >2018.09.30【LOJ517】「LibreOJ β Round #2」計算幾何瞎暴力(01Trie)(二進位制拆分)

2018.09.30【LOJ517】「LibreOJ β Round #2」計算幾何瞎暴力(01Trie)(二進位制拆分)

傳送門

解析:

看到標題的dalaodalao先不要急著錘我。。。
這道題的二進位制拆分和01Trie01Trie不能混在一起,不要急著說01Trie01Trie就是二進位制拆分。。。

思路:

這道題可以說是非常好的一道資料結構。
我相信應該沒有人會去想計算幾何。(那這出題人得有多善良)

先看操作1,要求在末尾插入一個數,這個與資料結構沒有什麼關係,我們可以直接在資料結構外建一個數組存。

操作2,詢問區間和,這個就是維護字首和,還是不能幫助我們確定使用什麼資料結構。

操作3,全域性xorxor
空氣凝固。。。這個除了01Trie01Trie還能怎麼做?

操作4,全域性排序。。。01

Trie01Trie內部的數字本來就是排了序的。我們只需要記錄之前xorxor了一個什麼東西就能夠確定內部具體的順序,在每次排序的時候將相數組裡面的數全部插入TrieTrie樹裡面。

那這道題就是01Trie01Trie咕咕咕了。

好,最(被和諧)的地方到了。

對於一個有全域性xorxor的題,我們要怎麼維護字首和?

這裡就是剛才提到的二進位制拆分的作用了。

對於所有的xorxor我們都不直接作用在陣列和TrieTrie樹上,而是用一個標記記錄當前應該要xorxor上一個什麼東西。

而字首和,我們將所有數的二進位制表示拆開,分位統計字首和(統計該位出現次數)。記錄在s

umsum數組裡面。那麼sum[x][i]sum[x][i]表示前xx位中在第ii位出現了多少個11,那麼出現的00的個數就是xsum[x][i]x-sum[x][i]。這兩個個數都是在異或上xortagxortag之前而言的

每次詢問一個位置,我們就看一下xortagxortag這一位是否是1,而採取是統計sum[x][i]sum[x][i]還是xsum[x][i]x-sum[x][i]

同理,在TrieTrie樹中,我們統計每個節點子樹出現各個位的次數。在記錄字首和的時候根據子樹大小調整就行了。
不要忘了在葉子節點的時候,只能統計該統計的部分,這個需要單獨處理一下。

假裝被和諧掉了的複雜度分析

根據我們剛才的敘述,我們可以得到一個不太對的複雜度。

單次xorxor的複雜度O(1)O(1)
TrieTrie樹單次插入的複雜度O(log2Ai)O(log^2A_i)(插入+二進位制字首和)
TrieTrie樹單次詢問的複雜度O(log2Ai)O(log^2A_i)
陣列單次插入O(logAi)O(logA_i)
陣列單次詢問複雜度O(logAi)O(logA_i)

單次排序複雜度O(×Trie)O(陣列中數個數\times Trie的插入複雜度)

而總複雜度可以近似認為是O((n+m)lognlogA)O((n+m)logn*logA)

程式碼:

#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;
}