【BZOJ】1878 HH的项链
来源:互联网 发布:火影忍者ol精炼数据 编辑:程序博客网 时间:2024/05/16 04:53
Overview
求区间不同数的个数。
Analysis
1. 莫队算法
多个区间询问,有在线和离线的方法。先考虑离线吧。
按照块排序,使用莫队算法可以轻松解决。
时间复杂度:
代码:
#include <cstdio>#include <cmath>#include <cctype>#include <algorithm>using namespace std;const int N=65536;const int M=200010;const int D=1000001;int n,a[N];int m,unit;struct Q{ int l,r,id; friend inline int operator < (Q qa,Q qb) { return qa.l/unit!=qb.l/unit?qa.l/unit<qb.l/unit:qa.r<qb.r; }}q[M];inline int Read(void){ int x=0; char c=getchar(); for (;!isdigit(c);c=getchar()); for (;isdigit(c);c=getchar()) x=x*10+c-'0'; return x;}int vis[D],cnt;int ans[M];int main(void){ n=Read(); for (int i=1;i<=n;i++) a[i]=Read(); m=Read(); for (int i=1;i<=m;i++) q[i].l=Read(),q[i].r=Read(),q[i].id=i; unit=(int)sqrt(n); sort(q+1,q+m+1); int l=1,r=0; for (int i=1;i<=m;i++) { for (;l>q[i].l;l--) { vis[a[l-1]]++; if (vis[a[l-1]]==1) cnt++; } for (;l<q[i].l;l++) { vis[a[l]]--; if (!vis[a[l]]) cnt--; } for (;r<q[i].r;r++) { vis[a[r+1]]++; if (vis[a[r+1]]==1) cnt++; } for (;r>q[i].r;r--) { vis[a[r]]--; if (!vis[a[r]]) cnt--; } ans[q[i].id]=cnt; } for (int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0;}
2. 另一个离线算法
其实还可以想一想其他的离线算法。
按照左端点排序行不行?尝试一下吧。
排完序后,固定左端点
接下来,我们考虑
①若
②若
我们只要找到下一个与
区间更改,单点求值,考虑使用区间数据结构。
树状数组就可以了,应该会跑得很快的。
这个算法不知道网上有没有,自己想出来的233333。
时间复杂度:
代码:实测
#include <cstdio>#include <cctype>#include <algorithm>using namespace std;const int N=65536;const int M=200010;const int D=1000001;int n;int a[N];int tag[D];int nxt[N];int m;struct Ques{ int l,r,id; friend inline int operator < (Ques qa,Ques qb) { return qa.l<qb.l; }}q[M];int ans[M];inline int read(void){ int x=0; char c=getchar(); for (;!isdigit(c);c=getchar()); for (;isdigit(c);c=getchar()) x=x*10+c-'0'; return x;}int cnt[N];struct TreeArray{ int t[N]; inline int lowbit(int i) { return i&-i; } inline void ins(int i,int add) { for (;i<=n;i+=lowbit(i)) t[i]+=add; } inline int query(int i) { int cnt=0; for (;i;i-=lowbit(i)) cnt+=t[i]; return cnt; }}tr;int main(void){ n=read(); for (int i=1;i<=n;i++) a[i]=read(); for (int i=1;i<=n;i++) tag[a[i]]=n+1; for (int i=n;i>=1;i--) nxt[i]=tag[a[i]],tag[a[i]]=i; m=read(); for (int i=1;i<=m;i++) q[i].id=i,q[i].l=read(),q[i].r=read(); sort(q+1,q+m+1); for (int i=1;i<=n;i++) tag[a[i]]=0; for (int i=1;i<=n;i++) { cnt[i]=cnt[i-1]; if (!tag[a[i]]) cnt[i]++; tag[a[i]]=1; } int now=1; for (int i=1;i<=m;i++) { for (;now<q[i].l;now++) tr.ins(now+1,-1),tr.ins(nxt[now],1); ans[q[i].id]=tr.query(q[i].r)+cnt[q[i].r]; } for (int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0;}
3. 可持久化数据结构的在线解法
不要从离线算法的排序入手,考虑从问题的解决入手。
区间
以权值为点,坐标为链建立可持久化线段树在线解决问题!!!
代码:实测
#include <cstdio>#include <cctype>const int N=65536;const int S=2097152;const int D=1048576;int n;int a[N];inline int Read(void){ int x=0; char c=getchar(); for (;!isdigit(c);c=getchar()); for (;isdigit(c);c=getchar()) x=x*10+c-'0'; return x;}int pre[N],tmp[D];struct Sgt{ struct Node { int lc,rc; int l,r; int cnt; }tr[S]; int tot; int Build(int l,int r) { int now=++tot; tr[now].l=l; tr[now].r=r; if (l!=r) { int mid=l+r>>1; tr[now].lc=Build(l,mid); tr[now].rc=Build(mid+1,r); } return now; } int Ins(int vtx,int w) { int now=++tot; tr[now].l=tr[vtx].l; tr[now].r=tr[vtx].r; tr[now].cnt=tr[vtx].cnt+1; if (tr[now].l!=tr[now].r) { int mid=tr[now].l+tr[now].r>>1; if (w<=mid) { tr[now].lc=Ins(tr[vtx].lc,w); tr[now].rc=tr[vtx].rc; } else { tr[now].lc=tr[vtx].lc; tr[now].rc=Ins(tr[vtx].rc,w); } } return now; } int Query(int vtx,int w) { if (tr[vtx].l==tr[vtx].r) return tr[vtx].cnt; int mid=tr[vtx].l+tr[vtx].r>>1; return w<=mid?Query(tr[vtx].lc,w):tr[tr[vtx].lc].cnt+Query(tr[vtx].rc,w); }}sgt;int root[N];void Init(void){ n=Read(); for (int i=1;i<=n;i++) a[i]=Read(); for (int i=1;i<=n;i++) { pre[i]=tmp[a[i]]; tmp[a[i]]=i; } root[0]=sgt.Build(0,n); for (int i=1;i<=n;i++) root[i]=sgt.Ins(root[i-1],pre[i]);}int m;int x,y;void Work(void){ m=Read(); for (int i=1;i<=m;i++) { x=Read(),y=Read(); printf("%d\n",sgt.Query(root[y],x-1)-(x-1)); }}int main(void){ Init(); Work(); return 0;}
跑得更慢了啊……
继续继续,一定还可以优化!
4. 改进算法三——又一个离线算法
求在
普遍的想法是把询问排序,然后通过
先贴两个代码:
代码1:线段树(
#include <cstdio>#include <cctype>#include <algorithm>using namespace std;const int N=65536;const int S=2097152;const int D=1048576;const int M=S;int n,m;int a[N];struct Q{ int l,r; int id; friend inline int operator < (Q qa,Q qb) { return qa.r<qb.r; }}q[M];int pre[N],tmp[D];inline int Read(void){ int x=0; char c=getchar(); for (;!isdigit(c);c=getchar()); for (;isdigit(c);c=getchar()) x=x*10+c-'0'; return x;}void Init(void){ n=Read(); for (int i=1;i<=n;i++) a[i]=Read(); for (int i=1;i<=n;i++) { pre[i]=tmp[a[i]]; tmp[a[i]]=i; } m=Read(); for (int i=1;i<=m;i++) { q[i].l=Read(); q[i].r=Read(); q[i].id=i; } sort(q+1,q+m+1);}struct Sgt{ struct Node { int l,r; int cnt; }tr[S]; void Build(int now,int l,int r) { tr[now].l=l; tr[now].r=r; if (l!=r) { int mid=l+r>>1; Build(now<<1,l,mid); Build(now<<1|1,mid+1,r); } } void Ins(int now,int w) { tr[now].cnt++; if (tr[now].l==tr[now].r) return; int mid=tr[now].l+tr[now].r>>1; if (w<=mid) Ins(now<<1,w); else Ins(now<<1|1,w); } int Query(int now,int w) { if (tr[now].l==tr[now].r) return tr[now].cnt; int mid=tr[now].l+tr[now].r>>1; return w<=mid?Query(now<<1,w):tr[now<<1].cnt+Query(now<<1|1,w); }}sgt;int now;int ans[M];void Work(void){ sgt.Build(1,0,n-1); for (int i=1;i<=n;i++) { sgt.Ins(1,pre[i]); for (;now<m&&q[now+1].r==i;now++) ans[q[now+1].id]=sgt.Query(1,q[now+1].l-1)-(q[now+1].l-1); } for (int i=1;i<=m;i++) printf("%d\n",ans[i]);}int main(void){ Init(); Work(); return 0;}
代码2:树状数组(实测
#include <cstdio>#include <cctype>#include <algorithm>using namespace std;const int N=65536;const int S=2097152;const int D=1048576;const int M=S;int n,m;int a[N];struct Q{ int l,r; int id; friend inline int operator < (Q qa,Q qb) { return qa.r<qb.r; }}q[M];int pre[N],tmp[D];inline int Read(void){ int x=0; char c=getchar(); for (;!isdigit(c);c=getchar()); for (;isdigit(c);c=getchar()) x=x*10+c-'0'; return x;}void Init(void){ n=Read(); for (int i=1;i<=n;i++) a[i]=Read(); for (int i=1;i<=n;i++) { pre[i]=tmp[a[i]]; tmp[a[i]]=i; } m=Read(); for (int i=1;i<=m;i++) { q[i].l=Read(); q[i].r=Read(); q[i].id=i; } sort(q+1,q+m+1);}struct TreeArray{ int tr[N]; inline int lowbit(int i) { return i&-i; } void Ins(int i,int add) { for (i++;i<=n;i+=lowbit(i)) tr[i]+=add; } int Query(int i) { int sum=0; for (i++;i;i-=lowbit(i)) sum+=tr[i]; return sum; }}tr;int nowq;int ans[M];void Work(void){ for (int i=1;i<=n;i++) { tr.Ins(pre[i],1); for (;nowq<m&&q[nowq+1].r==i;nowq++) ans[q[nowq+1].id]=tr.Query(q[nowq+1].l-1)-(q[nowq+1].l-1); } for (int i=1;i<=m;i++) printf("%d\n",ans[i]);}int main(void){ Init(); Work(); return 0;}
然而,我们可以继续优化!
优化的方法就是想办法把
排序已经做到最快了,我们能不能不排序?
当然可以,像保存图一样开个链表就行了……
最后一个代码:实测
#include <cstdio>#include <cctype>#include <algorithm>using namespace std;const int N=65536;const int M=200010;const int D=1000001;int n;int a[N];int tag[D];int nxt[N];int m;struct G{ int r,nxt;}mp[M];int tt,hd[N];int ans[M];inline int read(void){ int x=0; char c=getchar(); for (;!isdigit(c);c=getchar()); for (;isdigit(c);c=getchar()) x=x*10+c-'0'; return x;}inline int ins(int l,int r){ mp[++tt].r=r; mp[tt].nxt=hd[l]; hd[l]=tt;}int cnt[N];struct TreeArray{ int t[N]; inline int lowbit(int i) { return i&-i; } inline void ins(int i,int add) { for (;i<=n;i+=lowbit(i)) t[i]+=add; } inline int query(int i) { int cnt=0; for (;i;i-=lowbit(i)) cnt+=t[i]; return cnt; }}tr;int main(void){ n=read(); for (int i=1;i<=n;i++) a[i]=read(); for (int i=1;i<=n;i++) tag[a[i]]=n+1; for (int i=n;i>=1;i--) nxt[i]=tag[a[i]],tag[a[i]]=i; int l,r; m=read(); for (int i=1;i<=m;i++) l=read(),r=read(),ins(l,r); for (int i=n;i>=1;i--) { tr.ins(nxt[i]-1,1); for (int k=hd[i];k;k=mp[k].nxt) ans[k]=((n-i+1)-tr.query(mp[k].r-1))-(n-mp[k].r); } for (int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0;}
Sumarize
①区间问题可以从离线、在线的角度入手,也可以从问题的求解入手。
②区间问题离线的三种处理方法,不一定要排序,可以使用图来解决。
- BZOJ 1878 HH的项链
- bzoj-1878 HH的项链
- 【BZOJ】1878 HH的项链
- BZOJ 1878, HH的项链
- bzoj 1878 [SDOI2009]HH的项链
- BZOJ-1878-HH的项链-SDOI2009
- BZOJ 1878: [SDOI2009]HH的项链
- BZOJ 1878 [SDOI2009]HH的项链
- BZOJ 1878 [SDOI2009]HH的项链
- 【BZOJ 1878】[SDOI2009]HH的项链 莫队
- 【bzoj 1878】[SDOI2009]HH的项链
- BZOJ 1878 [SDOI2009]HH的项链 莫队
- bzoj 1878: [SDOI2009]HH的项链
- BZOJ 1878: [SDOI2009]HH的项链
- BZOJ 1878 【SDOI2009】HH的项链(离线+树状数组)
- bzoj 1878 [SDOI2009]HH的项链 树状数组 离线查询
- BZOJ 1878 [SDOI2009]HH的项链 题解与分析
- BZOJ 1878 [SDOI2009]HH的项链 离线+树状数组
- 血的教训:数据丢失灾难恢复总结
- +imageNamed:方法
- 简述strcpy、sprintf与memcpy的区别
- 每周学一点Egret(10) 插件分享
- hdu 1000 A+B problem
- 【BZOJ】1878 HH的项链
- 统计学习方法第一章笔记——统计学习方法概论
- 析构 构造 赋值
- 每天学一点linux(11)--find
- Xib和购物车UI界面的完善
- GRUB2详解
- App Store上传手机屏幕截图 尺寸
- 记录ubuntu14安装jdk的过程
- HDU 1009 FatMouse' Trade(贪心)