zoukankan      html  css  js  c++  java
  • Nowcoder Hash Function ( 拓扑排序 && 线段树优化建图 )

    题目链接

    题意 : 给出一个哈希表、其避免冲突的方法是线性探测再散列、现在问你给出的哈希表是否合法、如果合法则输出所有元素插入的顺序、如果有多解则输出字典序最小的那一个、如果不合法则输出 -1

    分析 : 

    经过对样例的模拟和观察、可以发现

    如果一个元素 A 本应去到的位置 pos 被元素 B 占据、则说明 B 要先于 A 进行放置

    由于是采用线性再探测、所以从 pos 开始到 A 现在的位置 posNow 中经过的所有位置都应该先于 A 放置

    例如 4 8 0 -1 在这个长度为 4 的哈希表中 0 元素本应在 0 这个位置、但是看到 4 在这个位置所以 4 要先于 0 插入

    然后往前推移发现到 0 元素当前位置为止、还有一个 8 在、所以 8 也一个先于 0 插入

    这给了我们一些启示、对于某个东西要在另一个东西之前执行

    那么自然想到构建出前驱图、然后进行拓扑排序、如果拓扑排序成功则说明有解、否则无解(存在环)

    要求字典序最小的话、使用队列构建拓扑排序选用的队列要改成优先队列

    但是还有一个问题、实际上你会发现、每一次构建、都是一个区间的数连接到一个节点上

    这样的建图、边会变得非常多、例如这种数据 1024、512、256、128、64、32、16、8 全部的哈希值都为 0 

    所以边会越来越多、接近 n ^ 2 建图、建图的复杂度就已经受不了了

    所以需要一个优化、那就是线段树优化建图

    适用于区间向区间、区间向点连边的情况

    首先建线段树、长度为 1 ~ n

    根据线段树在这题当中应用的意义

    初始化每个节点都向其父亲连接一条边

    表示子节点属于其父亲、这个是显然的

    然后将原始的数组元素都赋值到线段树的叶子节点去

    每一次区间向某一个数连边都可以变成某些表示此区间的线段树节点向表示这个数的叶子节点相连接

     最后把建完成的前驱图 ( 也就是在原来线段树的每个节点上连出相对应的边 ) 跑拓扑排序即可

    #include<bits/stdc++.h>
    #define LL long long
    #define ULL unsigned long long
    
    #define scl(i) scanf("%lld", &i)
    #define scll(i, j) scanf("%lld %lld", &i, &j)
    #define sclll(i, j, k) scanf("%lld %lld %lld", &i, &j, &k)
    #define scllll(i, j, k, l) scanf("%lld %lld %lld %lld", &i, &j, &k, &l)
    
    #define scs(i) scanf("%s", i)
    #define sci(i) scanf("%d", &i)
    #define scd(i) scanf("%lf", &i)
    #define scIl(i) scanf("%I64d", &i)
    #define scii(i, j) scanf("%d %d", &i, &j)
    #define scdd(i, j) scanf("%lf %lf", &i, &j)
    #define scIll(i, j) scanf("%I64d %I64d", &i, &j)
    #define sciii(i, j, k) scanf("%d %d %d", &i, &j, &k)
    #define scddd(i, j, k) scanf("%lf %lf %lf", &i, &j, &k)
    #define scIlll(i, j, k) scanf("%I64d %I64d %I64d", &i, &j, &k)
    #define sciiii(i, j, k, l) scanf("%d %d %d %d", &i, &j, &k, &l)
    #define scdddd(i, j, k, l) scanf("%lf %lf %lf %lf", &i, &j, &k, &l)
    #define scIllll(i, j, k, l) scanf("%I64d %I64d %I64d %I64d", &i, &j, &k, &l)
    
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1|1
    #define lowbit(i) (i & (-i))
    #define mem(i, j) memset(i, j, sizeof(i))
    
    #define fir first
    #define sec second
    #define VI vector<int>
    #define ins(i) insert(i)
    #define pb(i) push_back(i)
    #define pii pair<int, int>
    #define VL vector<long long>
    #define mk(i, j) make_pair(i, j)
    #define all(i) i.begin(), i.end()
    #define pll pair<long long, long long>
    
    #define _TIME 0
    #define _INPUT 0
    #define _OUTPUT 0
    clock_t START, END;
    void __stTIME();
    void __enTIME();
    void __IOPUT();
    using namespace std;
    const int maxn = 2e5 + 10;
    
    VI ans;
    int PreSum[maxn];
    int arr[maxn];
    int Node[maxn];
    int IN[maxn<<2];
    int Pos[maxn<<2];
    
    struct EDGE{ int v, nxt; }Edge[maxn<<4];
    int Head[maxn<<2], EdgeCnt;
    
    inline void init(int n)
    {
        ans.clear();
        for(int i=0; i<=(n<<2); i++){
            IN[i] = 0;
            Head[i] = -1;
        }
        EdgeCnt = 0;
    }
    
    inline void AddEdge(int from, int to)
    {
        Edge[EdgeCnt].v = to;
        Edge[EdgeCnt].nxt = Head[from];
        Head[from] = EdgeCnt++;
        IN[to]++;
    }
    
    void Build(int l, int r, int rt)
    {
        if(l == r){
            sci(arr[l]);
            PreSum[l] = PreSum[l-1] + ( arr[l] == -1 );
            Node[l] = rt;
            Pos[rt] = l;
            return ;
        }
    
        Pos[rt] = 0;
        AddEdge(rt<<1|1, rt);
        AddEdge(rt<<1, rt);
    
        int m = (l + r) >> 1;
        Build(lson);
        Build(rson);
    }
    
    void Connect(int L, int R, int c, int l, int r, int rt)
    {
        if(L <= l && r <= R){
            AddEdge(rt, c);
            return ;
        }
    
        int m = (l + r) >> 1;
        if(L <= m) Connect(L, R, c, lson);
        if(m < R) Connect(L, R, c, rson);
    }
    
    bool topo_sort(int n)
    {
        priority_queue<pii, vector<pii>, greater<pii> > que;
        for(int i=1; i<=n; i++){
            if(IN[Node[i]] == 0){
                que.push(mk(arr[i], Node[i]));
            }
        }
    
        while(!que.empty()){
            pii top = que.top(); que.pop();
    
            if(top.fir != -1) ans.pb(top.fir);
    
            for(int i=Head[top.sec]; i!=-1; i=Edge[i].nxt){
                int Eiv = Edge[i].v;
                if(--IN[Eiv] == 0){
                    if(Pos[Eiv] > 0) que.push(mk(arr[Pos[Eiv]], Eiv));
                    else que.push(mk(-1, Eiv));
                }
            }
        }
    
        return ((int)ans.size() == n - PreSum[n]);
    }
    
    int main(void){__stTIME();__IOPUT();
    
        int nCase;
        sci(nCase);
    
        while(nCase--){
    
            int n;
            sci(n);
    
            init(n);
    
            Build(1, n, 1);
    
            if(PreSum[n] == n) { puts(""); continue; }
    
            bool ok = true;
            for(int i=1; i<=n; i++){
                if(arr[i] == -1) continue;
                if((arr[i]%n + 1) == i) continue;
                int L = arr[i]%n + 1, R = (i - 1) == 0 ? n : i - 1;
                if(L <= R){
                    if(PreSum[R] - PreSum[L-1] > 0){ ok = false; break; }
                    Connect(L, R, Node[i], 1, n, 1);
                }else{
                    if(PreSum[n] - PreSum[L-1] + PreSum[R] > 0){ ok = false; break; }
                    Connect(L, n, Node[i], 1, n, 1);
                    Connect(1, R, Node[i], 1, n, 1);
                }
            }
    
            if(!ok) puts("-1");
            else{
                if(!topo_sort(n)) puts("-1");
                else for(int i=0; i<(int)ans.size(); i++) printf("%d ", ans[i]); puts("");
            }
        }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    __enTIME();return 0;}
    
    
    void __stTIME()
    {
        #if _TIME
            START = clock();
        #endif
    }
    
    void __enTIME()
    {
        #if _TIME
            END = clock();
            cerr<<"execute time = "<<(double)(END-START)/CLOCKS_PER_SEC<<endl;
        #endif
    }
    
    void __IOPUT()
    {
        #if _INPUT
            freopen("in.txt", "r", stdin);
        #endif
        #if _OUTPUT
            freopen("out.txt", "w", stdout);
        #endif
    }
    View Code
  • 相关阅读:
    java复习计划
    超过16位的字符串装16进制
    《将博客搬至CSDN》
    android设置中文字体样式
    布局文件View和ViewGroup
    创建线程的两种方法,继承Thread,继承Runnable
    本地文件的copy复制
    字节流和字符流完成URL下载,并存入本地
    文本过滤器的用法,FileFilter()和FilenameFilter()
    JavaSE笔记
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/9531821.html
Copyright © 2011-2022 走看看