bzoj 5181: [Baltic2016]Swap

题意

给定一个长度为n的序列Xn,该序列中没有重复的数字。你有n-1次操作的机会:在每次操作机会时对应的有着k=2,3,4,…n,(在第1次交换机会时k=2,以此类推)你可以交换X[k]和X[k/2],也可以不进行操作。求在执行完操作后,最小字典序的序列。

前言

这题搞了一天QAQ
主要是不知道怎么做的时候题解找了很久。。
最后是看代码看懂的

一个一开始的想法

我们直接贪心啊
对于三个位置 a,b,c a , b , c
分开情况讨论
主要就是尽量要小的放在前面
但这样显然是错的
因为放在某个位置,在后面可能会被换到更后面去

另一个想法

贪心玩了一会都不会。。
于是就考虑另外一个思路
就是如果我们可以对于每一个位置,维护一个他可以填的数的集合,那么也可以贪心
我是想边出答案,别维护
一开始我想的是线段树合并
但是这样是不行的
因为我们在要删除一个节点的时候,如果你把所有包含这个节点的数都删去这个节点
复杂度显然不可以接受
因为,在若干次变化之后,你根本不知道那些有这个数字,你还要把树都遍历一次

同时,这个做法有一个问题,就是没有用到每一次可以交换的地方是固定的
没有利用好性质的做法肯定不是一个优秀的做法

于是就有了更好的做法

但是这个思路还是行得通的
问题在于有一个数被选中的时候,我们怎么样快速地在所有点里面删除这个数
做法是,我们对于每一个点,建立二叉树
然后对于每一个交换操作,都建立一个新的点
两个状态分别表示换和不换
显然地,你两个操作只能选择一个
也就是说,你只可以选择一个
于是对于每一个交换节点,加一个标记就可以了
然后我们就可以贪心了
每一次遍历一棵树,然后再暴力删去他
考虑复杂度,每一个数的大小是
T(n)=T(n/2)+2 T ( n ) = T ( n / 2 ) + 2
因此是 log l o g 级别的
暴力添加不会T
同时很好地利用了交换的数是 x,x/2 x , x / 2 这个条件

CODE:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
const int N=200005*5;
struct qq
{
    int s1,s2;
    int val;
    bool d;//是否确定了儿子 
}tr[N];
int n;
int rt[N];
int tot;
int Query (int x)
{
    if (tr[x].val!=0)   return tr[x].val;
    if (tr[x].d==true)
        return Query(tr[x].s1);
    else return min(Query(tr[x].s1),Query(tr[x].s2));
}
bool Del (int x,int val)
{
    if (tr[x].val!=0)
    {
        if (tr[x].val==val) 
        {
            tr[x].val=n+1;
            return true;
        }
        return false;
    }
    if (tr[x].d==true) return Del(tr[x].s1,val);
    else
    {
        if (Del(tr[x].s1,val))
        {
          tr[x].d=true;
          return true;
        }
        else if (Del(tr[x].s2,val))
        {
            tr[x].d=true;
            swap(tr[x].s1,tr[x].s2);
            return true;
        }
        return false;
    }
}
int main()
{
    scanf("%d",&n);
    for (int u=1;u<=n;u++)
    {
        int x;
        scanf("%d",&x);
        tot++;
        tr[tot].val=x;
        rt[u]=tot;
    }
    for (int u=2;u<=n;u++)
    {
        int a=rt[u/2],b=rt[u];
        rt[u/2]=++tot;tr[tot].s1=a;tr[tot].s2=b;tr[tot].d=false;
        rt[u]=++tot;tr[tot].s1=a;tr[tot].s2=b;tr[tot].d=false;
    }
    for (int u=1;u<=n;u++)
    {
        int x=Query(rt[u]);
        Del(rt[u],x);
        if (u!=1) printf(" ");
        printf("%d",x);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值