1. 程式人生 > >牛客多校第四場 J Hash Function(拓撲排序 + 線段樹建邊)

牛客多校第四場 J Hash Function(拓撲排序 + 線段樹建邊)

題目描述

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;
}