Description

[JZOJ6023]【GDOI2019模拟2019.2.16】烤仓鼠【贪心】【Trie】_Trie


[JZOJ6023]【GDOI2019模拟2019.2.16】烤仓鼠【贪心】【Trie】_完全图_02

Solution

考虑如何求出那个最小值

显然我们可以求出全部数中二进制最高的不同位,答案的那一位肯定是1,那一位相同的数相邻可以随便放。
这样可以将全部数分成0的集合和1的集合,把一个集合中的数扔进Trie,再用另外一个集合的数去跑,就可以求出答案了。

接下来我们考虑贪心构造方案
此时我们相当于有两个完全图,然后这两个完全图之间有一些边,我们要找一条字典序最小的哈密尔顿路径

假设我们当前已经抵达了点u,寻找下一个点v
我们有两种选择:

  • 如果u与另一个集合还有边,可以跳到另一个集合
  • 可以在本集合选一个能走的编号尽量小的边。

考虑一个点怎样才能走

如果是跳到另一个集合,如果本集合还有点,跳过去又跳不回来了,那是不能走的。
如果是在本集合走,走到那个点和另一个集合有边,但是只剩那个点有边了,本集合还有另外的点,这样就导致有些点无法走到。

总结一番,我们可以得到判定能否走的三个命题,只要满足一个就可以走。

  • v所在的集合只剩这一个点
  • v所在的集合的另一个集合为空
  • 删去v与另一个集合的所有边,这两个集合仍然连通。

这样我们用一些set,优先队列之类的东西就可以维护了。

复杂度[JZOJ6023]【GDOI2019模拟2019.2.16】烤仓鼠【贪心】【Trie】_完全图_03

Code

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 300005
using namespace std;
int t[N*32][2],n1,n,a[N],d[N],cf[32],l,pt[N],da[N],dw[N],ans[N],ct[2],cnt[N*32][2],m1,pv[32*N];
priority_queue<int,vector<int>,greater<int> > hv[2][N];
set<int> h[2];
typedef set<int>::iterator IT;
bool bz[N],bp[N*32],bs[N];
bool cmp(int x,int y)
{
return a[x]<a[y];
}
int from(int k)
{
return (a[k]&cf[l])>0;
}
void put(int k,int c,int w)
{
fod(i,l-1,0)
{
int p=(c&cf[i])>0;
if(!t[k][p]) t[k][p]=++n1;
k=t[k][p];
}
if(!pv[k]) pv[k]=++pv[0];
hv[0][pv[k]].push(w);
pt[w]=k;
}
int query(int k,int c,int w)
{
int s=0;
fod(i,l-1,0)
{
int p=(c&cf[i])>0;
if(!t[k][p]) s^=cf[i],k=t[k][p^1];
else k=t[k][p];
}
pt[w]=k;
if(!pv[k]) pv[k]=++pv[0];
return s;
}
bool pd(int k)
{
int tp=from(k);
return (ct[tp]==1||ct[1-tp]==0||m1>1||(m1==1&&!(bz[k]&&cnt[pt[k]][1-tp]>0&&cnt[pt[k]][tp]==1)));
}
int main()
{
cin>>n;
fo(i,1,n) scanf("%d",&a[i]),d[i]=i;
sort(d+1,d+n+1,cmp);
int mx=0,mw=0;
fo(i,2,n)
if((a[d[i]]^a[d[i-1]])>=mx) mx=a[d[i]]^a[d[i-1]],mw=i;
cf[0]=1;fo(i,1,30) cf[i]=cf[i-1]<<1;
l=30;
while(l>0&&!(mx&cf[l])) l--;
n1=1;
mx=2e9;
fo(i,1,n)
{
dw[d[i]]=i;
if(!(a[d[i]]&cf[l])) put(1,a[d[i]],d[i]),ct[0]++,h[0].insert(d[i]);
else
{
ct[1]++,h[1].insert(d[i]);
int v=query(1,a[d[i]],d[i]);
if(v<mx) da[0]=1,da[1]=d[i],mx=v;
else if(mx==v) da[++da[0]]=d[i];
}
}
mx^=cf[l];
if(mx==0)
{
fo(i,1,n) printf("%d ",i);
return 0;
}
fo(i,1,da[0])
{
hv[1][pv[pt[da[i]]]].push(da[i]);
cnt[pt[da[i]]][1]++;
if(!bp[pt[da[i]]])
{
bp[pt[da[i]]]=1;
cnt[pt[da[i]]][0]=hv[0][pv[pt[da[i]]]].size();
if(cnt[pt[da[i]]][0]>0) m1++;
}
if(cnt[pt[da[i]]][0]>0) bz[da[i]]=1;
}

fo(i,1,n) if(dw[i]<mw&&bp[pt[i]]&&min(cnt[pt[i]][0],cnt[pt[i]][1])>0) bz[i]=1;

int x=n+1,y=n+1,z=n+1;
if(ct[0]>0)
{
x=*h[0].begin();
if(!pd(x)) x=*h[0].upper_bound(x);
}
if(ct[1]>0)
{
y=*h[1].begin();
if(!pd(y)) y=*h[1].upper_bound(y);
}
ans[1]=min(x,y);
if(bz[ans[1]]) cnt[pt[ans[1]]][from(ans[1])]--;
fo(i,1,n-1)
{
int k=ans[i],tp=from(k),w=n+1;
bs[k]=1;
ct[tp]--;
h[tp].erase(k);
if(ct[tp]>0)
{
w=*h[tp].begin();
if(!pd(w)) w=*h[tp].upper_bound(w);
}
if(bz[k]&&cnt[pt[k]][1-tp])
{
while(bs[hv[1-tp][pv[pt[k]]].top()]) hv[1-tp][pv[pt[k]]].pop();
int q=hv[1-tp][pv[pt[k]]].top();
if(pd(q)) w=min(w,q);
}
if(bz[k]&&cnt[pt[k]][tp]==0&&cnt[pt[k]][1-tp]>0) m1--;
if(bz[w]) cnt[pt[w]][from(w)]--;
ans[i+1]=w;
}
fo(i,1,n) printf("%d ",ans[i]);
}