cats 的集合 1

D06發表於2024-08-25
  • 0/1 Trie
  • 具象化一次操作對資料結構產生的影響
  • 試想,如果我們在一次修改指令中逐一更新了子樹p中的所有節點,但是在之後的查詢指令中卻根本沒有用到,那麼更新p的整棵子樹就是徒勞的
  • 精妙的懶標記設計,詳見程式碼註釋
  • (1ll<60)
  • 用類實現懶標記
  • 無法讀取檔案是因為UTF-8 BOM,另存為UTF-8就好了
  • 在有操作2、3的情況下,對操作4也要採取同樣的處理方法
  • 否則,舉個最簡單的反例:用3操作清零字典樹,之前的異或操作也會失效,不能作用在查詢的數上
  • 多測的清空
點選檢視程式碼
#include <bits/stdc++.h>
using namespace std;
int t[400000*31+5][2],tot,T;
const int maxn=(1ll<<31)-1;
//本題的懶標記思想好精妙,以下直接複製了題解的程式碼, 並嘗試對其做出解釋
//首先,把對第i層的修改放到根節點上下傳,並用二進位制數壓縮表示 
class LazyTag
{
	public:
		int o,a,x;
		LazyTag()
		{
			o=a=x=0;
		}
		LazyTag(int O,int A,int X)
		{
			o=O;
			a=A;
			x=X;
		}
		void merge(LazyTag &m)
		{
			o^=o&m.a;//and操作抵消or操作 
			a^=a&m.o;//or操作抵消and操作 
			int xx=(o&m.x)|(a&m.x);
			o^=xx;//異或操作抵消or操作,新增and+xor等價的or操作 
			o|=m.o;//合併or操作 
			a^=xx;//異或操作抵消and操作,新增or+xor等價的and操作 
			a|=m.a;//合併and操作 
			x^=((x&m.a)|(x&m.o))^m.x^xx;//更新xor操作 
		} 
		void clean()
		{
			o=a=x=0;
		}
}f[400000*31+5];
void spread(int p,int i);
int merge(int p,int q,int i)
{
	if(!p)
	{
		return q;
	}
	if(!q)
	{
		return p;
	}
	if(i==-1)
	{
		return p;
	}
	spread(p,i);
	spread(q,i);
	t[p][0]=merge(t[p][0],t[q][0],i-1);
	t[p][1]=merge(t[p][1],t[q][1],i-1);
	t[q][0]=t[q][1]=0;
	return p;
}
void insert(int x)
{
	int p=0;
	for(int i=30;i>=0;i--)
	{
		spread(p,i);
		int y=((x>>i)&1);
		if(!t[p][y])
		{
			t[p][y]=++tot;
			f[tot].clean();
			t[tot][0]=t[tot][1]=0;
		}
		p=t[p][y];
	}
}
int ask(int x)
{
	int p=0,ans=0;
	for(int i=30;i>=0;i--)
	{
		spread(p,i);
		int y=(((x>>i)&1)^1);
		if(t[p][y])
		{
			p=t[p][y];
			ans+=(1<<i);
		}
		else
		{
			p=t[p][y^1];
		}
	}
	return ans;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>T;
	while(T--)
	{
		int n,m;
		cin>>n>>m;
		tot=0;
		f[0].clean();
		t[0][0]=t[0][1]=0;
		for(int i=1;i<=n;i++)
		{
			int x;
			cin>>x;
			insert(x);
		}
		for(int i=1;i<=m;i++)
		{
			int opt,x;
			cin>>opt>>x;
			LazyTag tmp1(x,0,0);
			LazyTag tmp2(0,x^maxn,0);
			LazyTag tmp3(0,0,x);
			switch(opt)
			{
				case 1:
					insert(x);
					break;
				case 2:
					f[0].merge(tmp1);
					break;
				case 3:
					f[0].merge(tmp2);
					break;
				case 4:
					f[0].merge(tmp3);
					break;
				case 5:
					cout<<ask(x)<<endl;
					break;
			}
		}
	}
	return 0;
}
void spread(int p,int i)
{
	int o=(f[p].o>>i)&1;
	int a=(f[p].a>>i)&1;
	int x=(f[p].x>>i)&1;
	if(o)
	{
		t[p][1]=merge(t[p][1],t[p][0],i-1);
		t[p][0]=0;
	}
	if(a)
	{
		t[p][0]=merge(t[p][0],t[p][1],i-1);
		t[p][1]=0;
	}
	if(x)
	{
		swap(t[p][0],t[p][1]);
	}
	if(t[p][0])
	{
		f[t[p][0]].merge(f[p]);
	}
	if(t[p][1])
	{
		f[t[p][1]].merge(f[p]);
	}
	f[p].clean();
}

相關文章