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;
    }
  • 相关阅读:
    RMQ(模板 ST 区间最值,频繁的间隔时间)
    编程算法基地-2.1利用字符串API
    android学习记录(三)百度地图错误---只有一个电话显示帧,没有地图内容。
    【MongoDB】在windows平台mongodb切片集群(三)
    Android MenuItem 设置文本颜色-TextColor设置
    wikioi 1034 家 实时动态的网络流量(费用流)
    where can I find source of com.android.internal.R.styleable.AlertDialog_multiChoiceItemLayout?
    Android开发之自定义Spinner样式的效果实现(源代码实现)
    Java注释@interface的用法
    依赖注入及AOP简述(十三)——AOP应用举例(完结) .
  • 原文地址:https://www.cnblogs.com/captain1/p/9746381.html
Copyright © 2011-2022 走看看