Super Mario HDU 4417 (二分主席树 or 分块)

简单题意:给定一个长度为n的序列,有m次查询,要求算出在[L, R]的区间内,小于k值的个数有几个。

分析:数据在1e5左右,很明显想到的是主席树的结构,维护区间的信息。因为通过主席树我们可以直接查出第k大值,而且根据主席树是权值线段树的性质,符合单调的性质,所以可以二分第几大的数去逼近k值,最后算出位置就说明前面有多少数字比他小。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <cmath>
#include <bitset>
#include <map>
#include <set>

//#define ACM_LOCAL
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e5 + 5, M = 5e5 + 5, INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int a[N], rt[N];
int n, m;
struct Hash {
    int b[N], tot;
    void init() {tot = 0;}
    void insert(int x) {b[++tot] = x;}
    void build() {
        sort(b+1, b+1+tot);
        tot = unique(b+1, b+tot+1) - (b+1);
    }
    int pos(int x) {return lower_bound(b+1, b+tot+1, x) - b;}
}ha;
struct {
    int t[N << 5], lc[N << 5], rc[N << 5];
    int NodeNum = 0;
    ll sum[N << 5];
    int build(int l, int r) {
        int num = ++NodeNum;
        if (l != r) {
            int mid = (l + r) >> 1;
            lc[num] = build(l, mid);
            rc[num] = build(mid + 1, r);
        }
        return num;
    }
    int update(int pre, int l, int r, int x) {
        int num = ++NodeNum;
        lc[num] = lc[pre], rc[num] = rc[pre], t[num] = t[pre] + 1;
        if (l != r) {
            int mid = (l + r) >> 1;
            if (x <= mid) lc[num] = update(lc[pre], l, mid, x);
            else rc[num] = update(rc[pre], mid + 1, r, x);
        }
        return num;
    }
    int Qry_Kth_Num(int u, int v, int l, int r, int k) {
        if (l == r) return ha.b[l];
        int mid = (l + r) >> 1, num = t[lc[v]] - t[lc[u]];
        if (num >= k) return Qry_Kth_Num(lc[u], lc[v], l, mid, k);
        else return Qry_Kth_Num(rc[u], rc[v], mid + 1, r, k-num);
    }
    ll Qry_Kth_Sum(int u, int v, int l, int r, int k) {//k大数之和
        if (l == r) return 1ll*ha.b[l] * k;
        int mid = (l + r) >> 1, num = t[rc[v]] - t[rc[u]];
        if (num >= k) return Qry_Kth_Sum(rc[u], rc[v], mid+1, r, k);
        else return sum[rc[v]] - sum[rc[u]] + Qry_Kth_Sum(lc[u], lc[v], l, mid, k-num);
    }
    int Binary_Search(int left, int right, int val) {
        int l = 1, r = right - left + 1;
        while (l <= r) {
            int mid = l + r >> 1;
            int num = Qry_Kth_Num(rt[left - 1], rt[right], 1, ha.tot, mid);
            if (num > val) r = mid - 1;
            else l = mid + 1;
        }
        return r;
    }
}hjt;

void solve() {
    int T, c; scanf("%d", &T);
    while (T--) {
        scanf("%d %d", &n, &m);
        ha.init(); hjt.NodeNum = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            ha.insert(a[i]);
        }
        ha.build();
        rt[0] = hjt.build(1, ha.tot);
        for (int i = 1; i <= n; i++) {
            rt[i] = hjt.update(rt[i-1], 1, ha.tot, ha.pos(a[i]));
        }
        printf("Case %d:\n", ++c);
        while (m--) {
            int l, r, val;
            scanf("%d %d %d", &l, &r, &val);
            l++, r++;
            printf("%d\n", hjt.Binary_Search(l, r, val));
        }
    }


}

signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
#endif
    solve();
    return 0;
}

当然也可以考虑分块,用upper_bound来查询有多少数子比他小。时间复杂度大约是n n \sqrt{n} n ,控制一下常数也可以过。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <cmath>
#include <bitset>
#include <map>
#include <set>

#define ACM_LOCAL
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e5 + 5, M = 5e5 + 5, INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int a[N], belong[N], tag[N];
int n, m, block, num;
vector<int> ve[N];
int query(int x, int y, int val) {
    int ans = 0, pos1 = belong[x], pos2 = belong[y];
    for (int i = x; i <= min(y, block * pos1); i++)
        if (a[i] <= val) ans++;
    if (pos1 != pos2)
        for (int i = (pos2-1)*block+1; i <= y; i++)
            if (a[i] <= val) ans++;
    for (int i = pos1+1; i <= pos2-1; i++) {
        ans += upper_bound(ve[i].begin(), ve[i].end(), val) - ve[i].begin();
    }
    return ans;
}

void solve() {
    int T, c = 0; scanf("%d", &T);
    while (T--) {
        scanf("%d %d", &n, &m);
        block = sqrt(n);
        num = n / block + (n % block);
        for (int i = 1; i <= num; i++) ve[i].clear();
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            belong[i] = (i - 1) / block + 1;
            ve[belong[i]].push_back(a[i]);
        }
        for (int i = 1; i <= num; i++)
            sort(ve[i].begin(), ve[i].end());
        printf("Case %d:\n", ++c);
        for (int i = 1; i <= m; i++) {
            int l, r, val;
            scanf("%d %d %d", &l, &r, &val);
            l++, r++;
            printf("%d\n", query(l, r, val));
        }
    }


}

signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
#endif
    solve();
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值