NOJ 1190 约瑟夫问题 线段树OR树状数组

题目链接


约瑟夫问题
时间限制(普通/Java) : 1000 MS/ 3000 MS          运行内存限制 : 65536 KByte
总提交 : 607            测试通过 : 57 
比赛描述
约瑟夫问题是一个非常经典的问题,它的问题描述是:有 n 个人围成一圈,从第 1 个人开始,每次按顺时针方向向后选择第 m 个人,并将这个人出列。那么你能高效的算出出列的顺序吗?

输入
输入数据有多组,每组输入数据为一行,两个正整数 n和m (1<=n,m<=30000)

输出
每组输出只有一行,表示出列的顺序。每两个数字之间用一个空格分开。

样例输入
4 2
5 3

样例输出
2 4 3 1
3 1 5 2 4


很不错的一个题目,常用方法时数组或者链表模拟,会TLE,如果求最后剩下一个人的编号可以用数学方法计算。

思路

将每次出列的人拿掉,从左往右形成一个新的序列,每次从上一个出列的人所处为的位置考虑,找出下一个出列的人在新序列中的位置。

使用线段树或者树状树组可以很好的找到新序列中的第k个人的位置。

树状数组

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <vector>
#include <algorithm>
#include <queue>
#include <map>
#include <stack>
#include <string>
#include <math.h>
#include <bitset>
#include <ctype.h>
#include <set>
using namespace std;
typedef pair<int,int> P;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-9;
const int N = 3e4 + 5;
const int mod = 1e9 + 7;
int n, c[N];
inline int lowbit(int x)
{
    return x&(-x);
}
void add(int x, int val)
{
    while(x <= n)
    {
        c[x] += val;
        x += lowbit(x);
    }
}
int sum(int x)
{
    int ans = 0;
    while(x)
    {
        ans += c[x];
        x -= lowbit(x);
    }
    return ans;
}
int m;
int main()
{
    while(scanf("%d%d", &n, &m) != EOF)
    {
        memset(c, 0, sizeof(c));
        for(int i = 1; i <= n; i++)
            add(i,1);
        int num = n, i = 0;
        while(num > 1)
        {
            int j = (i+m-1)%num;
            int p = j+1;
            int l = 1, r = n, mid;
            while(l <= r)
            {
                mid = (l+r) >> 1;
                if(sum(mid) >= p) r = mid - 1;
                else l = mid + 1;
            }
            printf("%d ",r+1);
            add(r+1, -1);
            i = j % (num-1);
            num--;
        }
        int l = 1, r = n;
        while(l <= r)
        {
            int mid = (l+r) >> 1;
            if(sum(mid) >= 1) r = mid - 1;
            else l = mid + 1;
        }
        printf("%d
", r+1);
    }
    return 0;
}

线段树

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <vector>
#include <algorithm>
#include <queue>
#include <map>
#include <stack>
#include <string>
#include <math.h>
#include <bitset>
#include <ctype.h>
#include <set>
using namespace std;
typedef pair<int,int> P;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-9;
const int N = 3e4 + 5;
const int mod = 1e9 + 7;
int sum[N<<2];
int n,m;
int ans[N];
void build(int rt, int l, int r)
{
    if(l == r)
    {
        sum[rt] = 1;
        return;
    }
    int m = (l+r) >> 1;
    build(rt << 1 , l, m);
    build(rt << 1|1, m+1, r);
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}

void update(int pos, int val, int rt, int l, int r)
{
    if(l == r)
    {
        sum[rt] = 0;
        return ;
    }
    int m = (l+r) >> 1;
    if(pos <= m) update(pos, val, rt<<1, l, m);
    else update(pos, val, rt<<1|1, m+1, r);
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}

int query(int pos, int rt, int l, int r)
{
    if(l == r)
        return l;
    int m = (l+r) >> 1;
    if(pos > sum[rt<<1]) return query(pos - sum[rt<<1], rt<<1|1, m+1, r);
    else
        return query(pos, rt<<1, l, m);
}

int main()
{
    while(EOF != scanf("%d%d", &n,&m))
    {
        build(1,1,n);
        int len = n;
        int i = 0;
        int pos = 0;
        while(len > 1)
        {
            int p = (i + m-1)%len;
            ans[pos++] = query(p+1, 1, 1, n);
            update(ans[pos-1], 0, 1, 1, n);
            i = p%(len - 1);
            len--;
        }
        ans[pos++] = query(1,1,1,n);
        for(int i = 0; i < pos; i++)
        {
            if(i) printf(" ");
            printf("%d", ans[i]);
        }
        printf("
");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Alruddy/p/7416300.html