题解 [SDOI2009] HH的项链
对于这类问区间不同数的总数,显然是不能用线段树直接维护的,毕竟不符合区间区间可加性。
考虑对于一个右端点固定的询问,哪些数字实际上是有权值的。
比如区间 1 3 3 2 3 1 2
,显然,实际上对于相同的数字,只有一个是有权值的,其他权值均为 。但是这样还是无法起到简化的作用,毕竟对于 3 3 3
,要选那个 放权值呢?
由于我们固定右端点,所以取最右边的放权值一定可以囊括所有情况。对于上面的例子,我们应该这样放权值:
0 0 0 1 0 1 1 1 3 3 3 1 1 2
答案即为一个区间内的权值之和。
考虑如何实现上述的“固定右端点”,我们可以按照右端点从小到大排序,对其进行离线,对于新加入的右端点(颜色记作 ),若前面有与 相同的,将那个点的权值减一,这要求先预处理出每个点前面与其颜色相同的点的坐标,并使用树状数组进行单点修改区间查询。
点击查看代码
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef unsigned long long ULL; LL read() { LL sum=0,flag=1; char c=getchar(); while(c<'0'||c>'9') {if(c=='-') flag=-1; c=getchar();} while(c>='0'&&c<='9') {sum=sum*10+c-'0'; c=getchar();} return sum*flag; } const int N=1e6+10; int n,m; int a[N],p[N],lst[N]; int ans[N],tr[N]; struct node { int l,r,id; }q[N]; int cmp(node x,node y) { return x.r<y.r; } int lowbit(int x) { return x&(-x); } void add(int nd,int x) { for(int i=nd;i<=n;i+=lowbit(i)) tr[i]+=x; } int query(int nd) { int sum=0; for(int i=nd;i;i-=lowbit(i)) sum+=tr[i]; return sum; } int main() { // freopen("a.in","r",stdin); // freopen("a.out","w",stdout); n=read(); for(int i=1;i<=n;i++) { a[i]=read(); lst[i]=p[a[i]]; p[a[i]]=i; } cin>>m; for(int i=1;i<=m;i++) { q[i].l=read(); q[i].r=read(); q[i].id=i; } sort(q+1,q+1+m,cmp); int j=0; for(int i=1;i<=m;i++) { while(j+1<=q[i].r) { j++; add(j,1); if(lst[j]) add(lst[j],-1); } ans[q[i].id]=query(q[i].r)-query(q[i].l-1); } for(int i=1;i<=m;i++) cout<<ans[i]<<'\n'; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效