poj2182_线段树(点修改)_树状数组

链接:2182 -- Lost Cowshttp://poj.org/problem?id=2182

 纯暴力:

#include <iostream>
#define x first
#define y second
using namespace std;
typedef long long ll;
const int N = 8004;
int ans[N], a[N], pre[N];
int main() {
    // system("chcp 65001");
    cin.tie(0);
    cout.tie(0);
    freopen("C:/Users/zhaochen/Desktop/input.txt", "r", stdin);
    int n;
    cin >> n;
    a[1] = 1;
    for (int i = 2; i <= n; i++) {
        a[i] = i;
        cin >> pre[i];
    }
    for (int i = n; i > 0; i--) {
        int res = 0;
        for (int j = 1; j <= n; j++) {
            if (a[j] != -1) {
                res++;
                if (res == pre[i] + 1) {
                    ans[i] = a[j];
                    a[j] = -1;
                    break;
                }
            }
        }
    }
    for (int i = 1;i <= n;i++) {
        cout << ans[i] << endl;
    }
    return 0;
}

线段树:

#include <iostream>
#define x first
#define y second
using namespace std;
typedef long long ll;
const int N = 8004;
int ans[N], pre[N];
struct {
    int l, r, len;
} tree[4 * N];
void BuildTree(int left, int right, int root) { // 构建树根root
    tree[root].l = left; // 左边界
    tree[root].r = right; // 右边界
    tree[root].len = right - left + 1; // len存这个区间的数字个数,即这个结点下的数量
    if (left == right) {
        return;
    }
    int mid = (left + right) >> 1;
    int lchild = root << 1;
    BuildTree(left, mid, lchild); // 构建左子树根root*2
    BuildTree(mid + 1, right, lchild + 1); // 构建右子树根root*2+1
}
int find(int root, int num) { // 从树根root开始,找到第num个元素
    tree[root].len--; // 把此次更新的区间长度-1
    if (tree[root].l == tree[root].r) {
        return tree[root].l;
    }
    int lchild = root << 1;
    // 如果左子区间内的个数不够,则查询右子区间中第num - tree[lchild].len个元素
    if (tree[lchild].len < num) {
        return find(lchild + 1, num - tree[lchild].len);
    }
    // 查询左子区间中第num个元素
    return find(lchild, num);
}
int main() {
    // system("chcp 65001");
    cin.tie(0);
    cout.tie(0);
    freopen("C:/Users/zhaochen/Desktop/input.txt", "r", stdin);
    int n;
    cin >> n;
    for (int i = 2; i <= n; i++) {
        cin >> pre[i]; // 排在第i个前的有pre[i]个
    }
    BuildTree(1, n, 1);
    for (int i = n; i > 0; i--) {
        ans[i] = find(1, pre[i] + 1); // 第i个排在pre[i]+1
    }
    for (int i = 1; i <= n; i++) {
        cout << ans[i] << endl;
    }
    return 0;
}

树状数组图解:

什么是树状数组?让这个12岁年轻人为你讲解_程序员小灰的博客-CSDN博客作者Cat-shao,真名邵振轩,辽宁OIer,年仅12岁,非常感谢他的投稿。Part 1 我学它干啥?树状数组,Binary Indexed Tree(简称BIT),是由Peter M....https://blog.csdn.net/bjweimengshu/article/details/116617098套用树状数组模板:

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <set>
#define x first
#define y second
using namespace std;
typedef long long ll;
const int N = 8004;
int n;
int node[N] = { 0 }, precede[N] = { 0 }, ans[N] = { 0 };
// 树状数组巧妙地将数组不断按2的次方长度拆分,使得修改和求和都能在O(logn)的复杂度内完成
int lowbit(int x) { // 返回x的二进制数的最后一个1代表的大小,利用负数的补码(原码取反+1)
    return x & (-x);
}
// 块i与第一个包含他本身的块正好差lowbit(i),相加就可得到下一个需要更新的块
void add(int i, int value) { // 操作a[i]+value,更新和a[i]有关的node[]
    while (i <= n) { // 更新到最大的块
        node[i] += value;
        i += lowbit(i);
    }
}
int sum(int i) { // 求和操作,sum=a[1]+...+a[i],本题sum[i]代表i在当前的大小排位
    int sum = 0;
    while (i > 0) {
        sum += node[i];
        i -= lowbit(i);
    }
    return sum;
}
int findpos(int x) { // 二分法寻找sum(pos) = precede[i] + 1 对应的pos
    int left = 1, right = n;
    while (left < right) { // 找第一个<=x的sum(pos)对应的pos
        int mid = (left + right) >> 1;
        if (sum(mid) < x) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }
    return left;
}
int main() {
    // system("chcp 65001");
    cin.tie(0);
    cout.tie(0);
    freopen("C:/Users/zhaochen/Desktop/input.txt", "r", stdin);
    cin >> n;
    for (int i = 2; i <= n; i++) {
        cin >> precede[i]; // 排在第i个前的有precede[i]个
    }
    for (int i = 1; i <= n; i++) {
        node[i] = lowbit(i); // 本题特殊,无需用add()初始化node[],因为最初sum(i)为i
    }
    for (int i = n; i > 0; i--) {
        // 找剩下的第precede[i] + 1大的数
        int pos = findpos(precede[i] + 1);
        add(pos, -1); // 和a[pos]有关的node[]记录的长度-1
        ans[i] = pos;
    }
    for (int i = 1; i <= n; i++) {
        cout << ans[i] << endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值