Luogu SP3267 D-query(主席树)

SP3267 D-query

题目大意:

给出长度为 n 的序列,q 次询问,每次为区间 [l,r] 有多少不同的数字。

思路:

显然可以用莫队解决,考虑带 log 的做法。

对于一个区间 [l,r] 来说,区间内若有重复出现的数,那么他上一次出现的位置除了第一次出现的数以外,都在 [l,r] 范围内。我们记录每个位置上的值上一次出现的位置为 last[i] ,问题转化为求 [l,r]last[i]<l 的个数。区间上的权值线段树,使用主席树解决。

Code:
Copy
struct Node { //每个点维护的是值域上值的个数 int lf, rt; ll sum; //该节点的左节点为hjt[lf],右节点为hjt[rt],值为sum } hjt[N * 40]; int tot = 0, root[N]; //pre的作用是now要依赖以上一个版本的权值线段树来建立 void insert(int l, int r, int pre, int &now, ll p) { hjt[++tot] = hjt[pre]; //等于上一个版本线段树的当前节点 now = tot; ++hjt[now].sum; if (l == r) return; int m = (l + r) >> 1; if (p <= m) insert(l, m, hjt[pre].lf, hjt[now].lf, p); else insert(m + 1, r, hjt[pre].rt, hjt[now].rt, p); } //搜索到的当前节点所维护的区间为[l, r] //我们当前要查询[L, R]的权值线段树,Lnow表示L - 1版本的权值线段树遍历到的当前节点,Rnow表示R版本的权值线段树遍历到的当前节点 ll query(int l, int r, int Lnow, int Rnow, int ql, int qr) { if (ql <= l && r <= qr) { return hjt[Rnow].sum - hjt[Lnow].sum; } int m = (l + r) >> 1; ll ans = 0; if (ql <= m) ans += query(l, m, hjt[Lnow].lf, hjt[Rnow].lf, ql, qr); if (qr > m) ans += query(m + 1, r, hjt[Lnow].rt, hjt[Rnow].rt, ql, qr); return ans; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, m; cin >> n; vector<int> a(n + 1); vector<int> pre(N, 0); vector<int> las(n + 1); for (ll i = 1; i <= n; i++) { cin >> a[i]; las[i] = pre[a[i]] + 1; //整体向右偏移一位 pre[a[i]] = i; insert(1, n, root[i - 1], root[i], las[i]); } cin >> m; for (ll i = 1, l, r; i <= m; i++) { cin >> l >> r; cout << query(1, n, root[l - 1], root[r], 1, l) << "\n"; } return 0; }
posted @   Nepenthe8  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示