「2019冬令营提高组」密文

题目链接

题意分析

我们根据前缀异或和的性质可以知道

例如说原序列 \(a_1,a_2,a_3,a_4,...,a_n\)

那么前缀异或和之后是 \(s_1,s_2,s_3,s_4,...,s_n\)

我们每一次访问区间\([l,r]\) 那么就是求\(s_{l-1}\ xor\ s_r\)

很显然的是 我们知道了\(s_1,s_2,s_3,s_4,...,s_n\)

就可以求出\(\{a_i\}\)

现在我们只知道\(s_0=0\)

所以我们等于是要求\(0-n\)的最小生成树

其中\(cost(i,j)=sum_i\ xor\ sum_j\)

仔细想一下就会明白

至于怎么求解

我们首先把\(sum_0,sum_1,sum_2,sum_3,...,sum_n\)逐个插入\(01Trie\)树中

然后我们首先从最底层的节点开始

如果当前节点存在左右子树的话

那么我们把左右子树看作联通块

等于用权值最小的边联通两个联通快

类似于\(kruskal\)算法

我们首先爆搜一个联通快

另一个联通快就使用\(01Trie\)上贪心求最优边

最后就是联通的最小代价

由于是每一个点最多只会被\(log\)个父亲搜到

所以复杂度是\(O(nlog^2a_i)\)

CODE:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<string>
#include<queue>
#include<map>
#include<stack>
#include<list>
#include<set>
#include<deque>
#include<vector>
#include<ctime>
#define ll long long
#define inf 0x7fffffff
#define N 2050
#define IL inline
#define M 1008611
#define D double
#define ull unsigned long long
#define R register
using namespace std;
template<typename T>IL void read(T &_)
{
    T __=0,___=1;char ____=getchar();
    while(!isdigit(____)) {if(____=='-') ___=0;____=getchar();}
    while(isdigit(____)) {__=(__<<1)+(__<<3)+____-'0';____=getchar();}
    _=___ ? __:-__;
}
/*-------------OI使我快乐-------------*/
ll n;ll tot,ans,minn;
ll bel[M];
ll num[M],sum[M],tre[M*30][2],fro[M*30];
vector<ll> G[40];
IL ll find(ll x){return bel[x]==x ? x:bel[x]=find(bel[x]);}
IL void insert(ll now,ll x)
{
	ll root=0;
	for(R ll i=31;i>=0;--i)
	{
		if((1ll<<i)&now)
		{
			if(!tre[root][1])
			{
				tre[root][1]=++tot;
				G[i].push_back(tre[root][1]);
			} //把同一层的点放在一起
			root=tre[root][1];
		}
		else
		{
			if(!tre[root][0]) 
			{
				tre[root][0]=++tot;
				G[i].push_back(tre[root][0]);
			}
			root=tre[root][0];
		}
	}
	fro[root]=x;
} 
IL void dfs(ll lc,ll rc)
{
//	printf("now at %lld and %lld\n",lc,rc);
	if(!tre[lc][0]&&!tre[lc][1])
	{//我们取最小的一条边连通
		
		minn=min(minn,sum[fro[lc]]^sum[fro[rc]]);
		return;
	}
	if(tre[lc][0])
	{//01Trie树上的贪心
		if(tre[rc][0]) dfs(tre[lc][0],tre[rc][0]);
		else dfs(tre[lc][0],tre[rc][1]);	
	}
	
	if(tre[lc][1])
	{
		if(tre[rc][1]) dfs(tre[lc][1],tre[rc][1]);
		else dfs(tre[lc][1],tre[rc][0]);
	}
}
IL void getans(ll now)
{
	minn=999999999999999LL; 
	dfs(tre[now][0],tre[now][1]);
	ans+=minn;
//	dfs(tre[now][1],tre[now][0]);
}
int main()
{
	freopen("secret.in","r",stdin);
	freopen("secret.out","w",stdout);
	read(n);
	for(R ll i=1;i<=n;++i) read(num[i]),sum[i]=sum[i-1]^num[i];
	for(R ll i=0;i<=n;++i) insert(sum[i],i);
	for(R ll i=0;i<=n;++i) bel[i]=i;
//	for(R ll i=0;i<=30;++i)
//	{
//		if(!G[i].size()) break;
//		else for(R ll j=0;j<(ll)G[i].size();++j) printf("%d%c",G[i][j],(j==(ll)G[i].size()-1 ? '\n':' '));
//	}
	for(R ll i=1;i<=31;++i)
	{//优先从底层开始
		for(R ll j=0;j<(ll)G[i].size();++j)
		if(tre[G[i][j]][0]&&tre[G[i][j]][1]) getans(G[i][j]);//左右儿子均存在的话
	}
	printf("%lld\n",ans);
	fclose(stdin);
	fclose(stdout);
    return 0;
}

HEOI 2019 RP++

posted @ 2019-03-24 21:32  tcswuzb  阅读(252)  评论(0编辑  收藏  举报