1. 程式人生 > 實用技巧 >CF1446C Xor Tree

CF1446C Xor Tree

對於每個點\(i\),找到\(j\neq i\)\(a_j \ xor \ a_i\)最小,連邊\((i,j)\)

如果連邊之後形成一棵樹,那麼稱\(\{a_i\}\)為合法的。

給出\(\{a_i\}\),求至少刪掉多少個點才合法。

\(n\le 2*10^5\)

\(a_i\)互不相同


這題搞得可真是驚心動魄……搞了三個做法,最後一個終於對了……

幹了1.5h。

假設連有向邊\(i\to j\),那麼建出的圖是個基環樹森林。並且每棵基環樹的環長為\(2\)

如果合法,那麼必須滿足:只存在一對\((i,j)\),滿足對於各自而言,\(a_i\ xor \ a_j\)都是最小的。

自然這也會是全域性最小異或值。之前有個結論,對於一堆樸素的\(\{a_i\}\)

,其中最小的\(a_i \ xor \ a_j\)一定是排序之後相鄰的。

於是最終如何保留樹才合法呢?建出Trie,發現長這樣:

這樣dfs找一下就好了。


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200005
#define INF (1<<30)
int n;
int a[N];
struct Node{
	Node *c[2];
	int siz;
} d[N*40],*rt;
int cnt;
void insert(int x,int c=1){
	Node *t=rt;
	for (int i=29;i>=0;--i){
		t->siz+=c;
		if (!t->c[x>>i&1]){
			t->c[x>>i&1]=&d[++cnt];
			d[cnt]={NULL,NULL};
		}
		t=t->c[x>>i&1];
	}
	t->siz+=c;
}
int ans;
void dfs(Node *t,int s){
	if (t->c[0] && t->c[0]->siz==1 && t->c[1] && t->c[1]->siz==1)
		ans=max(ans,2+s);
	if (t->c[0]) dfs(t->c[0],s+(t->c[1]?1:0));
	if (t->c[1]) dfs(t->c[1],s+(t->c[0]?1:0));
}
int main(){
//	freopen("in.txt","r",stdin);
	rt=&d[cnt=1];
	scanf("%d",&n);
	for (int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		insert(a[i]);
	}
	ans=2;
	dfs(rt,0);
	printf("%d\n",n-ans);
	return 0;
}