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