大富翁 线段树+二分 +dfs

Posted echozqn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大富翁 线段树+二分 +dfs相关的知识,希望对你有一定的参考价值。

https://csustacm.fun/problem/2033

这个题目还是比较简单的,但是比赛的时候没有像清楚,用了一个不太熟悉的数据结构主席树,

所以出现了bug,主席树的bug是真的难找。

这个题目就是首先用dfs+线段树求出每一个富翁的val

然后用二分加线段树来找位置。

技术图片
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <algorithm>
#include <iostream>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 1e5 + 10;
typedef long long ll;
ll a[maxn], lazy[maxn * 4], sum[maxn * 4], flag[maxn * 4];
int n, val[maxn];
vector<int>G[maxn];
void add(int u, int v) {
    G[u].push_back(v);
    G[v].push_back(u);
}

void push_down(int id,int l,int r) {
    if (lazy[id] == 0) return;
    // printf("ss id=%d l=%d r=%d
", id, l, r);
    int mid = (l + r) >> 1;
    if (flag[id << 1]) {
        // printf("id=%d l=%d r=%d
", id, l, r);
        sum[id << 1] += (mid - l + 1)*lazy[id];
        lazy[id << 1] += lazy[id];
    }
    if (flag[id << 1 | 1]) {
        // printf("ww id=%d l=%d r=%d
", id, l, r);
        sum[id << 1 | 1] += (r - mid)*lazy[id];
        lazy[id << 1 | 1] += lazy[id];
    }
    lazy[id] = 0;
}

void update1(int id, int l, int r, int pos,int f) {
    if (pos<l || pos>r) return;
    if (f == 0) flag[id]++;
    else if (f == 1)flag[id]--;
    if (l == r) {
        sum[id] = 1;
        return;
    }
    push_down(id, l, r);
    int mid = (l + r) >> 1;
    if (pos <= mid) update1(id << 1, l, mid, pos, f);
    else update1(id << 1 | 1, mid + 1, r, pos, f);
    sum[id] = sum[id << 1] + sum[id << 1 | 1];
}

void update2(int id,int l,int r,int x,int y)
{
    // printf("id=%d l=%d r=%d x=%d y=%d
", id, l, r, x, y);
    if (x > r || y < l) return;
    if (x <= l && y >= r) {
        lazy[id] += 1;
        sum[id] += (r - l + 1);
        return;
    }
    push_down(id, l, r);
    int mid = (l + r) >> 1;
    if (x <= mid) update2(id << 1, l, mid, x, y);
    if (y > mid) update2(id << 1 | 1, mid + 1, r, x, y);
    sum[id] = sum[id << 1] + sum[id << 1 | 1];
}

int query(int id,int l,int r,int pos)
{
    if (pos<l || pos>r) return 0;
    if (l == r) return sum[id];
    push_down(id, l, r);
    int mid = (l + r) >> 1;
    if (pos <= mid) return query(id << 1, l, mid, pos);
    return query(id << 1 | 1, mid + 1, r, pos);
}

void dfs(int u, int pre) {
    // printf("u=%d a=%lld
", u, a[u]);
    update2(1, 1, n, 1, a[u]);
    update1(1, 1, n, a[u], 0);
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (v == pre) continue;
        dfs(v, u);
        val[a[u]] = min(val[a[u]], query(1, 1, n, a[u]));
        // printf("val[%lld]=%d v=%d
", a[u], val[a[u]], v);
        update1(1, 1, n, a[u], 2);
    }
    update1(1, 1, n, a[u], 1);
}

int c[maxn];

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

void update(int x,int k)
{
    while (x <= n) {
        c[x] += k;
        x += lowbit(x);
    }
}

int getsum(int x)
{
    int ans = 0;
    while (x > 0) {
        ans += c[x];
        x -= lowbit(x);
    }
    return ans;
}

int main() {
    int x;
    scanf("%d", &n);
    memset(val, inf, sizeof(val));
    for (int i = 1; i <= n; i++) scanf("%d", &x), a[x] = i, G[i].clear();
    for (int i = 1; i < n; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        add(u, v);
    }
    dfs(1, -1);
    for (int i = 1; i <= n; i++) if (val[i] >= inf) val[i] = 1;
    // for (int i = 1; i <= n; i++) printf("val[%d]=%d
", i, val[i]);
    for (int i = 1; i <= n; i++) {
        int l = 1, r = n, ans = 0;
        while (l <= r) {
            int mid = (l + r) >> 1;
            int res = mid - getsum(mid);
            if (res >= val[i]) ans = mid, r = mid - 1;
            else l = mid + 1;
        }
        printf("%d
", ans);
        update(ans, 1);
    }
    return 0;
}
View Code

 

以上是关于大富翁 线段树+二分 +dfs的主要内容,如果未能解决你的问题,请参考以下文章

Sequence(线段树+二分)

题目:排序(二分&线段树)

51nod1287(二分/线段树区间最值&单点更新)

模板线段树-单点修改,区间查询

线段树详解

常用算法模板