poj2886 (约瑟夫环,树状数组)

n个人围成圈,每个人手上有一个数字,最开始k跳出圈,如果k手上的数num是正数,则向左num个人出圈,负数就向右,直到所有人都出圈,得分最高的是第(1-n中因子数最多的数)次跳出的人

首先处理1-n中因子最多的数,有个名词叫反素数,不管他这里直接打表

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;
const int mx = 6e5 + 5;
int cnt[mx], anti_prim[mx], num[mx], tot = 0;

int main() {
    freopen("out.txt", "w", stdout);
    memset(cnt, 0, sizeof(cnt));
    for (int i = 1; i <= 600000; i++) {
        for (int j = 1; j*j <= i; j++) {
            if (i % j == 0) {
                if (j*j == i) cnt[i] += 1;
                else cnt[i] += 2;
            }
        }
    }
    int max_num = 0;
    for (int i = 1; i <= 600000; i++) {
        if (cnt[i] > max_num) {
            anti_prim[++tot] = i;
            num[tot] = cnt[i];
            max_num = cnt[i];
        }
    }
    for (int i = 1; i <= tot; i++) {
        printf("%d,",anti_prim[i]);
    }
    for (int i = 1; i <= tot; i++) {
        printf("%d,",num[i]);
    }

    return 0;
}

 

接着是处理跳出次序问题,用树状数组记录每个位置的人是否已经跳出,刚开始看到题解说二分我以为是把人按1-n排列,第k个人跳出就从k开始向左或向右进行二分出下一个跳出的人的位置,但是这样会有问题,比如第k个人手上是5,那么应该向右二分出5个人,但是有可能右边不足五个人需要绕回到左边,所以这样是行不通的

正确的做法是把人按1-n排列,然后每次有人跳出就通过公式计算出下一个人是从1-n中还在圈内的第几个,这样就可以二分1-n了

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;
const int mx = 5e5 + 5;
int anti_prim[] = {1,2,4,6,12,24,36,48,60,120,180,240,360,720,840,1260,1680,2520,5040,7560,10080,15120,20160,25200,27720,45360,50400,55440,83160,110880,166320,221760,277200,332640,498960,554400};
int num[] = {1,2,3,4,6,8,9,10,12,16,18,20,24,30,32,36,40,48,60,64,72,80,84,90,96,100,108,120,128,144,160,168,180,192,200,216};
int n, k, c[mx];

struct node {
    char name[20];
    int value; 
}stu[mx];

int lowbit(int x) {
    return x & -x;
}

void add(int x, int val) {
    for (int i = x; i <= n; i+=lowbit(i)) {
        c[i] += val;
    }
}

int sum(int x) {
    int ans = 0;
    for (int i = x; i > 0; i-=lowbit(i)) {
        ans += c[i];
    }
    return ans;
}

int bit_search(int x) {
    int left = 1, right = n, mid;
    while (left < right) {
        mid = (left + right) / 2;
        if (sum(mid) >= x) right = mid;
        else left = mid + 1;
    }
    return left;
}

int main() {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    while(scanf("%d%d", &n, &k) != EOF) {
        memset(c, 0, sizeof(c));

        for (int i = 1; i <= n; i++) {
            scanf("%s %d", stu[i].name, &stu[i].value);
            add(i, 1);
        }
        int pos = lower_bound(anti_prim, anti_prim + 36, n) - anti_prim;
        if (anti_prim[pos] > n) pos--;
        int now = k, next = k, remaining = n - 1, cnt = 1, ans;
        add(k, -1);
        while (true) {
            if (cnt == anti_prim[pos]) {
                ans = now;
                break;
            }
            int m = stu[now].value;
            if (m > 0) next = (next - 1 + m) % remaining;
            else next = ((next + m) % remaining + remaining) % remaining;
            if (next == 0) next = remaining;
            remaining--; cnt++;
            now = bit_search(next);
            add(now, -1);
        }
        printf("%s %d\n", stu[ans].name, num[pos]);
    }

    return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
POJ 2182是一道使用树状数组解决的题目,题目要求对给定的n个数进行排序,并且输出每个数在排序后的相对位置。树状数组是一种用来高效处理前缀和问题的数据结构。 根据引用中的描述,我们可以通过遍历数组a,对于每个元素a[i],可以使用二分查找找到a到a[i-1]中小于a[i]的数的个数。这个个数就是它在排序后的相对位置。 代码中的query函数用来求前缀和,add函数用来更新树状数组。在主函数中,我们从后往前遍历数组a,通过二分查找找到每个元素在排序后的相对位置,并将结果存入ans数组中。 最后,我们按顺序输出ans数组的元素即可得到排序后的相对位置。 参考代码如下: ```C++ #include <iostream> #include <cstdio> using namespace std; int n, a += y; } } int main() { scanf("%d", &n); f = 1; for (int i = 2; i <= n; i++) { scanf("%d", &a[i]); f[i = i & -i; } for (int i = n; i >= 1; i--) { int l = 1, r = n; while (l <= r) { int mid = (l + r) / 2; int k = query(mid - 1); if (a[i > k) { l = mid + 1; } else if (a[i < k) { r = mid - 1; } else { while (b[mid]) { mid++; } ans[i = mid; b[mid = true; add(mid, -1); break; } } } for (int i = 1; i <= n; i++) { printf("%d\n", ans[i]); } return 0; } ``` 这段代码使用了树状数组来完成题目要求的排序功能,其中query函数用来求前缀和,add函数用来更新树状数组。在主函数中,我们从后往前遍历数组a,通过二分查找找到每个元素在排序后的相对位置,并将结果存入ans数组中。最后,我们按顺序输出ans数组的元素即可得到排序后的相对位置。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [poj2182Lost Cows——树状数组快速查找](https://blog.csdn.net/aodan5477/article/details/102045839)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [poj_2182 线段树/树状数组](https://blog.csdn.net/weixin_34138139/article/details/86389799)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值