Joseph问题 (线段树)

Posted captain1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}

 

以上是关于Joseph问题 (线段树)的主要内容,如果未能解决你的问题,请参考以下文章

解析·优化 ZKW线段树

树状数组和线段树有啥区别?

线段树

逆序对 线段树解法

线段树-代码实现细节与技巧

线段树