牛客网暑期ACM多校训练营(第四场)J Hash Function 拓扑排序 线段树

题目链接: J Hash Function

题目大意

一张hash表,长度为n,哈希函数为hash(x) = x mod n,如果有冲突,则位置向后移一位(n-1的下一位是0)
现在给你一张hash表,要求出字典序最小插入顺序

思路

如果a[i]%n != i,说明在a[i]插入前,a[i]%n到i-1所在的位置已经被占用了,也就是说区间[a[i]%n, i-1]中的数字必须在a[i]之前插入,处理出所有这样的情况,建图,如果a[u]必须在a[v]之前插入,那么连一条u到v的边,然后拓扑排序
但这样最坏可能有n^2的边,图太大了,但可以发现,建边都是一个区间[a[i]%n, i-1]向一个点i建边,那么可以利用线段树的思想,将所有节点建成一个线段树,将线段树中的子节点向父节点建边,这样就可以快速的将一个区间与一个点建边了

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
const int maxn = 2e5 + 100;
int n, a[maxn], T, pre[maxn];

int pos[maxn << 2], id[maxn << 2], in[maxn << 2];
vector<int> G[maxn << 2];

#define ls l, m, rt<<1
#define rs m+1, r, rt<<1|1

void addedge(int u, int v)
{
    G[u].push_back(v);
    in[v]++;
}

void build(int l = 0, int r = n - 1, int rt = 1)
{
    id[rt] = -1;
    if (l == r)
    {
        pos[l] = rt;
        id[rt] = l;
        return ;
    }
    int m = (l + r) >> 1;
    addedge(rt<<1, rt);
    addedge(rt<<1|1, rt);
    build(ls);
    build(rs);
}

void addedge(int L, int R, int p, int l = 0, int r = n - 1, int rt = 1)
{
    if (L <= l && r <= R)
    {
        addedge(rt, p);
        return ;
    }
    int m = (l + r) >> 1;
    if (L <= m) addedge(L, R, p, ls);
    if (m < R) addedge(L, R, p, rs);
}

bool check(int s, int t)
{
    if (s <= t) return (pre[t] - pre[s] == 0) && (a[s] != -1);
    return pre[t] == 0 && pre[n - 1] - pre[s - 1] == 0;
}

int main()
{
    for (scanf("%d", &T); T; --T)
    {
        scanf("%d", &n);
        for (int i = 0; i < n; ++i) scanf("%d", a + i);
        for (int i = 0; i <= n * 4; ++i)  G[i].clear(), in[i] = 0;
        for (int i = 0; i < n; ++i) pre[i] = i ? (a[i] == -1) + pre[i - 1] : (a[i] == -1);
        bool flag = 0;
        for (int i = 0; i < n; ++i)
        {
            if (a[i] != -1 && !check(a[i] % n, i))
            {
                flag = 1;
                break;
            }
        }
        if (flag)
        {
            puts("-1");
            continue;
        }
        build();
        for (int i = 0; i < n; ++i)
        {
            if (a[i] != -1)
            {
                int s = a[i] % n, t = i;
                if (s == t) continue;
                else if (s < t) addedge(s, t - 1, pos[i]);
                else
                {
                    if (t != 0) addedge(0, t - 1, pos[i]);
                    addedge(s, n - 1, pos[i]);
                }
            }
        }
        priority_queue<P, vector<P>, greater<P>> que;
        for(int i=0; i<n; ++i) 
        {
            if(in[pos[i]] == 0) que.push(P(a[i], pos[i]));
        }
        vector<int> ans;
        while(!que.empty())
        {
            P now = que.top(); que.pop();
            int u = now.second;
            if(now.first != -1) ans.push_back(now.first);
            for(int v : G[u])
            {
                --in[v];
                if(in[v] == 0) que.push(P(id[v]==-1 ? -1 : a[id[v]], v));
            }
        }
        int len = ans.size();
        if(len != n-pre[n-1])
        {
            puts("-1");
        }
        else 
        {
            for(int i=0; i<len; ++i)
            {
                printf("%d%c", ans[i], i==len-1 ? '\n' : ' ');
            }
            if(len == 0) puts("");
        }

    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值