• 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;
    }
  • 相关阅读:
    阿里图标库引用简介---20200608更新
    2.10排序算法
    2.9Node节点的学习
    2.8DOM节点的学习
    2.5数组
    2.6对象和函数
    2.7变量、内存、Math和Date的初级认识
    css优先级问题
    事件委托(事件代理)初认识
    静态页面学习随笔(1)-如何正确布局大体结构
  • 原文地址:https://www.cnblogs.com/captain1/p/9746381.html
Copyright © 2020-2023  润新知