Luogu SP3267 D-query(主席树)
题目大意:
给出长度为 的序列, 次询问,每次为区间 有多少不同的数字。
思路:
显然可以用莫队解决,考虑带 log 的做法。
对于一个区间 来说,区间内若有重复出现的数,那么他上一次出现的位置除了第一次出现的数以外,都在 范围内。我们记录每个位置上的值上一次出现的位置为 ,问题转化为求 上 的个数。区间上的权值线段树,使用主席树解决。
Code:
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具