Luogu P3567 [POI2014]KUR-Couriers
作者:互联网
说一道 POI 的 水题
题 目 链 接 : https://www.luogu.com.cn/problem/P3567#submit
由 于 翻 译 中 题 意 已 经 很 简 洁 了 ,这 里 不 做 多 余 的 简 化。
题 目 很 明 显 是 一 道 数 据 结 构 题 。
对 于 这 一 类 题 型 ,通 常 要 考 虑 将 原 问 题 的 转 化 。
这 一 点 其 实 是 很 重 要 的 。毕 竟 不 是 所 有 题 都 是 板 子 题 。
对 于 这 个 问 题 :每 次 询 问 一 个 区 间 内 有 没 有 一 个 数 出 现 次 数 超 过 一 半 。
我 们 考 虑 :假 设 存 在 一 个 区 间 ,其 间 存 在 一 个 满 足 题 意 的 数 。
注 意 :对 于 每 个 区 间 ,最 多 仅 存 在 一 个 数 满 足 题 意 。这 一 点 相 信 很 容 易 理 解 。
我 们 再 考 虑 : 在 区 间 内 的 数 中 ,哪 个 数 是 有 可 能 满 足 题 意 的 ?
答 案 也 很 显 然 。
既 然 出 现 次 数 超 过 一 半 ,区 间 中 自 然 不 存 在 出 现 次 数 比 它 多 的 数。
即 假 设 一 个 数 符 合 题 意 ,他 定 然 同 时 也 是 区 间 众 数 。
由 于 这 个 题 目 不 带 修 改 。 我 们 考 虑 使 用 logn 的 主 席 树 。
预 处 理 是 很 标 准 的 权 值 主 席 树 。如 果 你 不 会 ,或 不 是 很 理 解 建 议 参 照 我所看见的比较好的主席树入门
每 次 查 询 ,在 树 上 往 大 的 儿 子 走 ,因 为 只 有 大 的 儿 子 ,才 可 能 存 在 超 过 一 半 的 叶 节 点 。
最 后 判 断 一 下 ,到 达 的 叶 子 节 点 的 权 值 是 否 大 于 区 间 一 半 长 度 一 半 就 行 了。
详 见 代 码
#include<bits/stdc++.h> #define MAXN 15000005 #define INF 1000000 using namespace std; namespace A { int tot; int L[MAXN],R[MAXN],val[MAXN]; int build(int l,int r,int x,int last) { int rt=++tot; L[rt]=L[last]; R[rt]=R[last]; val[rt]=val[last]; if (l==r) { val[rt]++; return rt; } int mid=(l+r)>>1; if (x<=mid) L[rt]=build(l,mid,x,L[last]); else R[rt]=build(mid+1,r,x,R[last]); val[rt]=val[L[rt]]+val[R[rt]]; return rt; } int ask(int rt1,int rt2,int l,int r,int x) { if (l==r) return l; int mid=(l+r)>>1; if (val[L[rt2]]-val[L[rt1]]>=x) return ask(L[rt1],L[rt2],l,mid,x); if (val[R[rt2]]-val[R[rt1]]>=x) return ask(R[rt1],R[rt2],mid+1,r,x); return 0; } } int n,m,rt[MAXN]; int main() { scanf("%d %d",&n,&m); for (int i=1;i<=n;i++) { int x; scanf("%d",&x); rt[i]=A::build(1,INF,x,rt[i-1]); } for (int i=1;i<=m;i++) { int l,r; scanf("%d %d",&l,&r); printf("%d\n",A::ask(rt[l-1],rt[r],1,INF,(r-l+1)/2+1)); } return 0; }
标签:rt,return,val,int,Luogu,P3567,Couriers,MAXN,rt1 来源: https://www.cnblogs.com/maowuyou/p/12466255.html