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

Description

在这里插入图片描述
在这里插入图片描述

Solution

考虑如何求出那个最小值

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

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

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

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

考虑一个点怎样才能走

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

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

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

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

复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

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]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值