牛客网暑期ACM多校训练营(第四场)J:Hash Function(数据结构+乱搞)

 

题目大意:

有一种hash方式为,当a[i]%n产生冲突时,就往后推到一位没有冲突的地方。现在给你hash后的序列,能否推出hash前的顺序,如果可以,输出最小字典序。

 

解题思路:

刚开始比赛的时候想的是拓扑排序,因为对于拓扑排序来说在A事件没有发生前,是不可能发生B事件的。那么就是说如果我a[i]%n的结果是pos 那么就可以对于 pos 到 i 所有的数对于a[i]连一条有向边,这样就表示在这些数被选之前,a[i]这个数不可能被选,其实完美符合拓扑排序的定义,但是极限情况下的边数为n^2条,同时这道题目n=2e6,因此必须优化,其实当时比赛的时候也想到了  就是 对于 A->B B->C 是没有必要连A->C这条边的。但是根本不知道该怎么优化这条边,然后就gaygay了。比赛结束后发现很多队其实是用一种很巧妙的方法乱怼过去的。。。

这里说一种做法,万恶的csdn,写到一半崩了,结果就没草稿了,搞得不想写了。。。

首先预处理将所有正确位置的数加入到优先队列中,当我们从队列中拿出一个数的时候,分别记录两个变量l=(pos-1)%n,r=(pos+1)%n,之后判断 l r 两个位置的数是否之前已经被取出作为答案,如果是的话,就不断的左移右移直到找到一个没有被作为答案的 l 和 r 。之后的操作就直接判断 a[r]%n 这个位置的数是否已经被作为答案取出去了,如果是,就将a[r]加入优先队列中即可。

 

Ac代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
int n,tot,a[maxn],nex[maxn],pre[maxn];
vector<int> ans;
bool in[maxn],vis[maxn];
void init()
{
    ans.clear();tot=0;
    for(int i=0;i<=n;i++) vis[i]=0;
    for(int i=0;i<=n;i++) in[i]=0;
    for(int i=0;i<=n;i++) nex[i]=pre[i]=0;
}
int main()
{
    int QAQ;
    scanf("%d",&QAQ);
    while(QAQ--)
    {
        scanf("%d",&n);
        init();
        priority_queue< pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>> > que;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            if(a[i]==-1) continue;
            if(a[i]%n==i)
            {
                in[i]=1;    //在队列中的数
                que.push({a[i],i});
            }
            tot++;  //记录不为-1的数的个数
        }
        while(!que.empty())
        {
            int now=que.top().second;   //从队列中拿出一个数
            que.pop(); vis[now]=1;
            ans.push_back(a[now]);
            if(ans.size()==tot) break;  //如果不为-1 的数取完了 break
            int r=(now+1)%n,l=(n+now-1)%n;
            while(vis[r]) r=nex[r]; //判断 l r 是否都被取过
            while(vis[l]) l=pre[l];
            nex[now]=r,pre[now]=l;  //记录位置 方便跳跃
            if(!in[r]&&a[r]!=-1&&(n+r-l-1)%n>=((n+r-a[r]%n)%n)) //判断a[r]的正确位置的数是否已经被取走 
            {
                que.push({a[r],r});
                in[r]=1;
            }
        }
        if(tot!=ans.size()) printf("-1\n");
        else if(tot==0) printf("\n");
        else
        {
            for(int i=0;i<(int)ans.size();i++)
                printf("%d%c",ans[i],i==(int)ans.size()-1?'\n':' ');
        }
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值