zoukankan      html  css  js  c++  java
  • Joseph问题 (线段树)

    Joseph问题似乎是入门题,就是那个报数出圈的问题,不过它暴力模拟的复杂度是O(nm)的,如果题目的数据范围达到了30000,那就超时了。怎么用线段树维护呢?

    我们可以这么考虑,每次我们其实要查询在当前这个点过了m个人是哪一个人。我们需要维护一下当前序列中一共有多少人,还需要维护每个人实际的位置在哪(因为人们出圈了之后他就不占位置了)

    我们可以用一棵权值线段树来完成。

    首先是修改,这个没什么好说的,直接单点修改改成0就行,然后同时返回修改的位置,这是一个人出圈的位置。不过怎么找到这个位置呢?我们可以首先确定下来这个人在当前序列的第几位,那么我们直接在线段树上二分就可以了,然后返回那个位置。

    一开始我们要query一下从1到上一次停留位置有几个人,然后把这个值+m-1,mod现在所有的人数再+1就是当前位置,找一下那个人在哪就可以了。

    看一下代码。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    #include<cmath>
    #include<queue>
    #include<set>
    #define lowbit(x) x & (-x)
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    
    using namespace std;
    typedef long long ll;
    const int M = 60005;
    const ll INF = 100000000000009;
    
    int read()
    {
        int ans = 0,op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
            if(ch == '-') op = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            ans *= 10;
            ans += ch - '0';
            ch = getchar();
        }
        return ans * op;
    }
    
    struct seg
    {
        int v;
    } t[M<<2];
    
    int n,m,last,g;
    
    void build(int p,int l,int r)
    {
        if(l == r)
        {
            t[p].v = 1;
            return;
        }
        int mid = (l+r) >> 1;
        build(p<<1,l,mid),build(p<<1|1,mid+1,r);
        t[p].v = t[p<<1].v + t[p<<1|1].v;
    }
    
    int query(int p,int l,int r,int pos)
    {
        if(l == r) return t[p].v = 0,l;
        int mid = (l+r) >> 1,cur;
        if(pos <= t[p<<1].v) cur = query(p<<1,l,mid,pos);
        else cur = query(p<<1|1,mid+1,r,pos-t[p<<1].v);
        t[p].v = t[p<<1].v + t[p<<1|1].v;
        return cur;
    }
    
    int count(int p,int l,int r,int kl,int kr)
    {
        if(kl > kr) return 0;
        if(l == kl && r == kr) return t[p].v;
        int mid = (l+r) >> 1;
        if(kr <= mid) return count(p<<1,l,mid,kl,kr);
        else if(kl > mid) return count(p<<1|1,mid+1,r,kl,kr);
        else return count(p<<1,l,mid,kl,mid) + count(p<<1|1,mid+1,r,mid+1,kr);
    }
    
    int main()
    {
        n = read(),m = read();
        build(1,1,n);
        rep(i,1,n) printf("%d ",last = query(1,1,n,(count(1,1,n,1,last)+m-1)%t[1].v+1));
        return 0;
    }
  • 相关阅读:
    Java实现各种内部排序算法
    Java实现堆排序(大根堆)
    Java对象的序列化和反序列化
    Java实现链式存储的二叉查找树(递归方法)
    337. House Robber III(包含I和II)
    318. Maximum Product of Word Lengths
    114. Flatten Binary Tree to Linked List
    106. Construct Binary Tree from Inorder and Postorder Traversal
    105. Construct Binary Tree from Preorder and Inorder Traversal
    96. Unique Binary Search Trees(I 和 II)
  • 原文地址:https://www.cnblogs.com/captain1/p/9746381.html
Copyright © 2011-2022 走看看