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

传送门:https://www.nowcoder.com/acm/contest/142/J
题目大意就是给你一个散列表,还原出字典序最小的原序列
解法:通过推样例发现,一个数x如果不在x%n 的位置,那么从x%n 到当前数字位置i-1的区间内都已经被占满,那么考虑从x%ni-1 所有的点到i 建一条边,然后跑一次拓扑排序(这里建边相当于是限制第i个数字一定比x%ni-1 的数字后出现)。然后发现这样子建边是o(n^2)的。于是采用线段树优化建边。
线段树建边的过程:在build函数中,让儿子节点与父亲节点连边,在update函数中,让区间对目标点建边,这样就可以保证l-r区间内的所有点到目标点都有了一条间接的边。(除开根节点的其他点都可以当做一个虚点)

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
const int inf=-0x3f3f3f3f;
int a[maxn];
int n,maxx;
int id[maxn],val[maxn<<2],indeg[maxn<<2];
vector<int>e[maxn<<2],ans;
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>>q;
void addedge(int x,int y)
{
    e[x].push_back(y);
    indeg[y]++;
}
void build(int rt,int L,int R)
{
    maxx=max(maxx,rt);
    val[rt]=inf;
    if(L==R){
        id[L]=rt;
        val[rt]=a[L];
    }else{
        int mid=L+R>>1;
        build(rt<<1,L,mid);
        build(rt<<1|1,mid+1,R);
        addedge(rt<<1,rt);
        addedge(rt<<1|1,rt);
    }
}
void update(int rt,int L,int R,int l,int r,int u)
{
    if(l>r) return;
    if(l<=L&&R<=r) addedge(rt,u);
    else{
        int mid=L+R>>1;
        if(l<=mid) update(rt<<1,L,mid,l,r,u);
        if(r>mid) update(rt<<1|1,mid+1,R,l,r,u);
    }
}
int main()
{
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=0;i<n;i++) scanf("%d",&a[i]);
        build(1,0,n-1);
        int cnt=0;
        for(int i=0;i<n;i++){
            if(a[i]==-1) continue;
            cnt++;
            int t=a[i]%n;
            if(i==t) q.push({val[id[i]],id[i]});
            else if(i>t) update(1,0,n-1,t,i-1,id[i]);
            else{
                update(1,0,n-1,t,n-1,id[i]);
                update(1,0,n-1,0,i-1,id[i]);
            }
        }
        while(!q.empty()){
            pair<int,int>temp=q.top();
            q.pop();
            int u=temp.second;
            if(temp.first!=inf) ans.push_back(temp.first);
            for(auto v:e[u]){
                indeg[v]--;
                if(indeg[v]==0) q.push({val[v],v});
            }
        }
        if(ans.size()!=cnt) puts("-1");
        else if(cnt==0) puts("");
        else{
            for(auto it:ans) cout<<it<<' ';cout<<endl;
        }
        for(int i=0;i<=maxx;i++) e[i].clear(),indeg[i]=0;
        ans.clear();
        maxx=0;
        while(!q.empty()) q.pop();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值