牛客多校第四场 J Hash Function(拓扑排序 + 线段树建边)

链接:https://www.nowcoder.com/acm/contest/142/J
来源:牛客网
 

题目描述

Chiaki has just learned hash in today's lesson. A hash function is any function that can be used to map data of arbitrary size to data of fixed size. As a beginner, Chiaki simply chooses a hash table of size n with hash function .
Unfortunately, the hash function may map two distinct values to the same hash value. For example, when n = 9 we have h(7) = h(16) = 7. It will cause a failure in the procession of insertion. In this case, Chiaki will check whether the next position is available or not. This task will not be finished until an available position is found. If we insert {7, 8, 16} into a hash table of size 9, we will finally get {16, -1, -1, -1, -1, -1, -1, 7, 8}. Available positions are marked as -1.
After done all the exercises, Chiaki became curious to the inverse problem. Can we rebuild the insertion sequence from a hash table? If there are multiple available insertion sequences, Chiaki would like to find the smallest one under lexicographical order.
Sequence a1, a2, ..., an is lexicographically smaller than sequence b1, b2, ..., bn if and only if there exists i (1 ≤ i ≤ n) satisfy that ai < bi and aj = bj for all 1 ≤ j < i.

输入描述:

There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:
The first line of each case contains a positive integer n (1 ≤ n ≤ 2 x 105) -- the length of the hash table. 
The second line contains exactly n integers a1,a2,...,an (-1 ≤ ai ≤ 109).
It is guaranteed that the sum of all n does not exceed 2 x 106.

输出描述:

For each case, please output smallest available insertion sequence in a single line. Print an empty line when the available insertion sequence is empty. If there's no such available insertion sequence, just output -1 in a single line.

示例1

输入

复制

3
9
16 -1 -1 -1 -1 -1 -1 7 8
4
8 5 2 3
10
8 10 -1 -1 34 75 86 55 88 18

输出

复制

7 8 16
2 3 5 8
34 75 86 55 88 18 8 10

题目大意:题目定义对于一个大小为n的hash表,一开始表内的值都为-1,每次往表内插入一个数x时,x对应的hash值为h(x)=x%n。但如果当前x所对应的hash值被其它的值用过了,x的hash值就会等于h(x)=(x+1)%n,以此类推,直到找到第一个没有被其它任何值使用过的hash值。hash表内的值为a[h(x)]=x;

现在给出一个进行了若干次插入操作之后的hash表,要你判断这个hash表是否合法,如果合法的话输出字典序最小的插入顺序,否则输出-1,如果hash表为空表就输出一个空行。

题目思路:由于有一一对应的前置关系,所以我们很容易就能想到用拓扑排序来解决这个问题。但是如果暴力建边来维持所有的前置关系的话时间复杂度是O(N^2)的,根本不可能在时限内解决问题。这个时候我们就要引入一个骚操作了——线段树建边。

我们都知道线段树的每一个结点都代表着一个区间[l,r],同时本题建边的时候都是一个连续区间内的点向某一结点建边的(因为如果当前的值x不在x%n的位置上的话,就代表着在x%n~pos[x]这个区间内的数都是要先于x被插入hash表的)。所以我们可以利用线段树的性质,每次建边只需要将对应的区间结点连向目标结点即可。在初始化线段树的时候,我们只需要将子结点到父结点之间再建一条边,这样就能保证建边的合法性了。这样建边的复杂度是O(N*logN)。建边完成之后再跑一遍拓扑排序即可。(由于要求输出的字典序最小,所以把拓扑排序时bfs的队列换成优先队列即可)。

具体实现看代码:

#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lowbit(x) x&-x
#define MP make_pair
#define pb push_back
#define debug(x) cout<<"x= "<<x<<endl;
#define FIN freopen("in.txt","r",stdin);
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef pair<int,int>pii;
const int mod=1e9+7;
const ll infll=0x3f3f3f3f3f3f3f3f;
const int MX=2e6+7;
const double EPS=1e-8;
const int INF = 0x3f3f3f3f;

int n;
int a[MX],sum[MX],id[MX],_id[MX];
int IN[MX],sz;
vector<int>E[MX];
void add_edge(int u,int v) {
    E[u].pb(v);
    IN[v]++;
}
void init(int _n) {
    for(int i=0; i<=_n*10; i++) {
        IN[i]=0;
        E[i].clear();
    }
}
void add(int L,int R,int x,int l,int r,int rt) {
    if(L<=l && r<=R) {
        add_edge(rt,id[x]);
        return;
    }
    int m=(l+r)>>1;
    if(L<=m) add(L,R,x,lson);
    if(R>m) add(L,R,x,rson);
}
void init_tree(int l,int r,int rt) {
    if(l==r) {
        id[l]=rt;
        _id[rt]=l;
        return;
    }
    _id[rt]=0;
    int m=(l+r)>>1;
    init_tree(lson);
    init_tree(rson);
}
void build(int l,int r,int rt) {
    if(l==r) return;
    add_edge(rt<<1,rt);
    add_edge(rt<<1|1,rt);
    int m=(l+r)>>1;
    build(lson);
    build(rson);
}
int vec[MX];
bool top_sort(int n) {
    sz=0;
    priority_queue<pii,vector<pii>,greater<pii> >q;
    for(int i=1; i<=n; i++) {
        if(a[i]==-1) continue;
        if(IN[id[i]]==0) {
            q.push(MP(a[i],id[i]));
        }
    }
    while(!q.empty()) {
        pii nw=q.top();
        q.pop();
        int u=nw.se;
        if(nw.fi!=-1) vec[++sz]=nw.fi;
        for(auto v:E[u]) {
            IN[v]--;
            if(IN[v]==0) {
                if(_id[v]>0) q.push(MP(a[_id[v]],v));
                else q.push(MP(-1,v));
            }
        }
    }
    if(sz!=n-sum[n])
        return 0;
    return 1;
}

int main() {
    //FIN;
    int _;
    for(scanf("%d",&_); _; _--) {
        scanf("%d",&n);
        init(n);
        for(int i=1; i<=n; i++) {
            scanf("%d",a+i);
            sum[i]=sum[i-1]+(a[i]==-1);
        }
        if(sum[n]==n) {
            puts("");
            continue;
        }
        init_tree(1,n,1);
        bool flag=1;
        for(int i=1; i<=n; i++) {
            if(a[i]==-1) continue;
            int cnt=a[i]%n+1;
            if(cnt==i) continue;
            if(cnt<i) {
                if(sum[i]-sum[cnt-1]>0) {
                    flag=0;
                    break;
                }
                add(cnt,i-1,i,1,n,1);
            } else {
                if(sum[n]-sum[cnt]+sum[i]>0) {
                    flag=0;
                    break;
                }
                if(i>1) add(1,i-1,i,1,n,1);
                add(cnt,n,i,1,n,1);
            }
        }
        if(!flag) {
            puts("-1");
            continue;
        }
        build(1,n,1);
        if(top_sort(n)) {
            for(int i=1; i<=sz; i++)
                printf("%d%c",vec[i],i==sz?'\n':' ');
        } else
            puts("-1");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lee_w_j__/article/details/81266091