记录一下学到的新姿势:线段树优化建图

题目大意:
已知哈希函数为 hash(x) = x mod n ,且如果出现哈希冲突,则 hash(x) = hash(x+1)。
现给出一张 hash 表,长度为 n,问字典序最小插入顺序。

知识点: 线段树优化建图、拓扑排序

解题思路:

对于一个数 x,设它所在的位置为 pos,如果 pos ≠ x%n,则 pos 左边,x%n 及其右边所有的数都要在 x 之前插入(如果这个区间里面有 -1,那么输出 "-1" ),所以我们可以从这个区间中的所有点连一条边到 x,跑完拓扑排序即可得到答案(如果图有环也输出 "-1" )。但是,普通的建图方式是 O(n2),这显然不够优秀,题解跟我们介绍了一种用线段树优化的建图方法:建一棵线段树,将哈希表中所有的数作为叶子结点,从所有的子结点往父节点连一条边。若要将一个区间中的数连到 x ,只需将这个区间对应的所有父结点连到 x 即可,复杂度 O(log n)。

AC代码:

#include <bits/stdc++.h>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long LL;
const int INF=0x3f3f3f3f;
const int MAXN=2e5+5;
vector G[MAXN*6];
struct Point{
    int du,num,id;
    friend bool operator <(const Point a,const Point b){
        return a.num>b.num;
    }
}pt[MAXN*6];
int a[MAXN];
bool flag[MAXN<<2];
int tree[MAXN<<2],ind;
void pushup(int rt){
    G[tree[rt<<1]].push_back(tree[rt]);
    G[tree[rt<<1|1]].push_back(tree[rt]);
    pt[tree[rt]].du+=2;
    flag[rt]=flag[rt<<1]||flag[rt<<1|1];
}
void build(int l,int r,int rt){
    if(l==r){
        tree[rt]=l;
        G[tree[rt]].clear();
        pt[tree[rt]].du=0;
        pt[tree[rt]].num=a[l];
        pt[tree[rt]].id=l;
        if(a[l]<0)  flag[rt]=true;
        else    flag[rt]=false;
        return;
    }
    tree[rt]=ind++;
    G[tree[rt]].clear();
    pt[tree[rt]].du=0;
    pt[tree[rt]].num=-2;
    pt[tree[rt]].id=tree[rt];
    int m=(l+r)/2;
    build(lson);
    build(rson);
    pushup(rt);
}
bool add(int L,int R,int to,int l,int r,int rt){
    if(L<=l&&r<=R){
        G[tree[rt]].push_back(to);
        pt[to].du++;
        return flag[rt];
    }
    int m=(l+r)/2;
    bool ret=false;
    if(L<=m)    ret=ret||add(L,R,to,lson);
    if(m<R)     ret=ret||add(L,R,to,rson);
    return ret;
}
priority_queue que;
int ans[MAXN];
int main(){
    int mod,T;
    scanf("%d",&T);
    while(T--){
        while(!que.empty()) que.pop();
        scanf("%d",&mod);
        int have=0;
        for(int i=0;i<mod;i++){
            scanf("%d",&a[i]);
            if(a[i]>=0) have++;
        }
        ind=mod;
        build(0,mod-1,1);
        bool temp=false;
        for(int i=0;i<mod;i++){
            if(a[i]>=0){
                int pos=a[i]%mod;
                if(pos<i)
                    temp=add(pos,i-1,i,0,mod-1,1);
                else if(pos>i){
                    if(i>0)
                        temp=temp||add(0,i-1,i,0,mod-1,1);
                    temp=temp||add(pos,mod-1,i,0,mod-1,1);
                }
                if(temp){
                    puts("-1");
                    break;
                }
            }
        }
        if(temp)    continue;
        for(int i=0;i<ind;i++){
            if(pt[i].du==0)
                que.push(pt[i]);
        }
        Point now;
        int cnt=0;
        while(!que.empty()){
            now=que.top();
            que.pop();
            if(now.num<0){
                for(int i=0;i<G[now.id].size();i++){
                    int v=G[now.id][i];
                    pt[v].du--;
                    if(pt[v].du==0) que.push(pt[v]);
                }
            }
            else{
                ans[cnt++]=now.num;
                for(int i=0;i<G[now.id].size();i++){
                    int v=G[now.id][i];
                    pt[v].du--;
                    if(pt[v].du==0) que.push(pt[v]);
                }
            }
        }
        if(cnt<have)
            puts("-1");
        else if(cnt==0)
            puts("");
        else{
            printf("%d",ans[0]);
            for(int i=1;i<cnt;i++){
                printf(" %d",ans[i]);
            }puts("");
        }
    }
    return 0;
}
全部评论

相关推荐

菜鸡29号:根据已有信息能初步得出以下几点: 1、硕士排了大本和大专 2、要求会多语言要么是招人很挑剔要么就是干的活杂 3、给出校招薪资范围过于巨大,说明里面的薪资制度(包括涨薪)可能有大坑
点赞 评论 收藏
分享
03-22 14:51
已编辑
复旦大学 前端工程师
之前做过的项目都是和前端有关的,本人也比较喜欢设计和所见即所得的编程体验。css&nbsp;学的比较好,Vue,&nbsp;html,&nbsp;网络属于还凑合能应付面试,但是&nbsp;js&nbsp;没有系统学过,现在在暑期实习面试中狠狠被拷打。现在大三,后续考虑出国读研,感觉现在&nbsp;all&nbsp;in&nbsp;前端是不是有点把路走窄了?求广大牛友指路btw:两周可以把&nbsp;js&nbsp;学好莫?能过笔试面试的水平(差不多用两周的1/3的时间)
股真人:1. 朋友,你bg fdu,而且可以读美研,这个平台绝大多数人的认知指导不了你,在这里问能得到什么呢?(而且光看你提的这个问题,无法让人看出你是fdu这个水平的学生)2. 如果是确认了喜欢前端,那么就all in;如果只是有点感兴趣,那找个实习来玩玩,让自己认识更清楚;如果是畏难心理选的,那只能说思维方式有问题。3. 强调自己是女生是什么意思?自己看不起自己?你要去的美国认可这种文化吗?
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务