codeforces 220c Little Elephant and Shifts

       很少遇到经典的题目,这个不算经典但是却也非常精彩。题目

题目大意:给定两个两个1-n的排列,定义两个排列的距离为:所有相同数字之间距离的最小值,More formally, it's such minimum |i - j|, that ai = bj.定义A cyclic shift number i (1 ≤ i ≤ n) of permutation b consisting from n elements is a permutation bibi + 1... bnb1b2... bi - 1. Overall a permutation has n cyclic shifts. 输出者n个cyclic shifts的距离。

eg:

4
2 1 3 4
3 4 2 1
输出 2 1 0 1

解题思路:由于n很大(10^5),所以,我想到应该是O(nlogn)的算法,我想到线段树,但是怎么也想不出来怎么维护,后来和同学讨论,想到一种方法:第一个序列为A,第二个需要旋转的为B,即使维护B中的值是在A中对应值得左边还是右边,如果是左边,那么旋转之后距离+1,右边则-1,如果我们维护这两个优先队列,当-1的元素-到0时,则插入到左边的序列中,右边的序列删除。对于头上需要移动到最后的元素,则插入操作,和删除操作。说的可能有点乱!

但是有几个难点:1)删除操作怎么处理?优先队列,不管是用堆实现还是stl都没有删除的操作,我的方法是,每一个元素都有一个时间戳,如果出队的元素时间戳过时了,那么直接出对,不用管。这样就达到了删除的作用

2)对于整个优先队列的+1和-1操作,可以考虑使用延迟加减的操作,理解就是,既然整个都+1相当于没有+1,也就是出队的时候在+1就是了,这样的话,插入需要注意了,插入前要加减这个延迟,才能保证出队是的值的正确性。

code:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#include <stdlib.h>
using namespace std;
#define N 100010
int dat1[N],dat2[N];

int pos[N];
struct note
{
    int val,time,index;
    note(){}
    note(int _v,int _t,int _i):val(_v),time(_t),index(_i){}
};
struct cmp
{
    bool operator() ( note a, note b )
    {
        return a.val > b.val;
    }
};
int _time[N];
int main()
{
    int n;
    while(cin >> n)
    {
        for(int i = 0;i < n;i++) scanf("%d",&dat1[i]);
        for(int i = 0;i < n;i++) scanf("%d",&dat2[i]);

        for(int i = 0;i < n;i++) pos[dat1[i]] = i;

        priority_queue < note ,vector<note> ,cmp > l_queue,r_queue;

        memset(_time,0,sizeof(_time));
        for(int i = 0;i < n;i++)
        {
            //在原数的左侧
            if(pos[ dat2[i] ] >= i) l_queue.push( note(pos[dat2[i]] - i,0, pos[ dat2[i] ] ) );
            else r_queue.push( note(i-pos[dat2[i]],0, pos[ dat2[i] ] ) );
        }
        int t = 0;
        int now_time = 1;
        for(int i = 0;i < n;i++)
        {
            while(l_queue.empty() == 0 && l_queue.top().time != _time[l_queue.top().index]) l_queue.pop();

            while(r_queue.empty() == 0 && r_queue.top().time != _time[r_queue.top().index]) r_queue.pop();

            if(l_queue.size() == 0) cout << r_queue.top().val - t << "\n";
            else if(r_queue.size() == 0) cout << l_queue.top().val + t << "\n";
            else cout << min(l_queue.top().val+t,r_queue.top().val-t) << "\n";

            if(i == n-1) continue;
            t++;
            int ttt = dat2[i];
            r_queue.push(note(n-1 - pos[ttt] + t,now_time,pos[ttt] ));
            _time[pos[ttt]] = now_time++;

            while(r_queue.empty() == 0 && r_queue.top().val - t == 0)
            {
                note tt = r_queue.top();r_queue.pop();
                tt.val = 0-t;
                l_queue.push(tt);
            }
        }
    }
    return 0;
}

ps:我很喜欢这个题~


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值