[bzoj 1878][SDOI2009]HH的项链
Description
HH有一串由各种漂亮的贝壳组成的项链。HH相信不同的贝壳会带来好运,所以每次散步 完后,他都会随意取出一
段贝壳,思考它们所表达的含义。HH不断地收集新的贝壳,因此他的项链变得越来越长。有一天,他突然提出了一
个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答。。。因为项链实在是太长了。于是,他只
好求助睿智的你,来解决这个问题。
Input
第一行:一个整数N,表示项链的长度。
第二行:N个整数,表示依次表示项链中贝壳的编号(编号为0到1000000之间的整数)。
第三行:一个整数M,表示HH询问的个数。
接下来M行:每行两个整数,L和R(1 ≤ L ≤ R ≤ N),表示询问的区间。
N ≤ 50000,M ≤ 200000。
Output
M行,每行一个整数,依次表示询问对应的答案。
Sample Input
6
1 2 3 4 3 5
3
1 2
3 5
2 6
1 2 3 4 3 5
3
1 2
3 5
2 6
Sample Output
2
2
4
2
4
题解
这题,这题,这题,毒瘤!!!
卡我分块!!!
1.分块做法
虽然是莫队经典题但是还是想写分块啊
可以保存每个点往前第一个与他颜色相同的点的坐标
假设它为$pre$中,那么我们要找的就是区间内的$pre$小于$L$的值的数量,因为$pre$大于$L$的数,一定在区间内出现了至少两次。这个可以画个图想想,想明白了基本就懂这题怎么用分块写了
那么对块内元素进行排序,查询的时候二分查找就行,散块直接暴力查找
查询的总复杂度是$O(m*log(\sqrt{n})*\sqrt{n})$
这复杂度很擦边...然后万恶的bzoj时限开4s,就死活卡不过去了...已经尝试了$fread$,$fwrite$,$inline$,$register$,以及各种奇奇怪怪的块的大小
都卡不过去...
评测记录
中间那次WA是因为调试的东西没删
不断增长的代码量说明了一切....
在洛谷交了一下是80分
如果哪位大大分块卡过去这题的话跟我讲一下卡法...
这里放一下我的分块代码,就不贴那些加了一堆优化的了,贴最原始的
#include <bits/stdc++.h> using namespace std; int n,m,a[50010],pre[50010],p[1000010]; int block,num,l[50010],r[50010],belong[50010],sum[50010]; void build(){ block=sqrt(n); num=n/block; if(n%block)num++; for(int i=1;i<=num;i++){ l[i]=block*(i-1)+1; r[i]=block*i; } r[num]=n; for(int i=1;i<=n;i++){ belong[i]=(i-1)/block+1; sum[i]=pre[i]; } for(int i=1;i<=num;i++){ sort(sum+l[i],sum+r[i]+1); } } int find(int L,int R,int x){ while(L<=R){ int mid=(L+R)>>1; if(sum[mid]<x)L=mid+1; else R=mid-1; } return L; } int query(int L,int R){ int ans=0; if(belong[L]==belong[R]){ for(int i=L;i<=R;i++){ if(pre[i]<L)ans++; } return ans; } for(int i=L;i<=r[belong[L]];i++){ if(pre[i]<L)ans++; } for(int i=l[belong[R]];i<=R;i++){ if(pre[i]<L)ans++; } for(int i=belong[L]+1;i<=belong[R]-1;i++){ ans+=find(l[i],r[i],L)-l[i]; } return ans; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); pre[i]=p[a[i]]; p[a[i]]=i; } build(); scanf("%d",&m); while(m--){ int fl,fr; scanf("%d%d",&fl,&fr); printf("%d\n",query(fl,fr)); } return 0; }
2.莫队做法
卡了一个晚上后我终于接受了现实。去打莫队
如果是莫队的话这题就模板题了,就离线一下排序一下,两个指针跳来跳去就行了
然后在bzoj就可以A了
然后尝试去卡洛谷的
洛谷数据加强到了$n,m<=500000$
再次卡常
$fread$,$fwrite$,$inline$,$register$,块的大小改为$block=n/sqrt(m*2/3)$,排序使用奇偶排序,再加个O2
然后就卡过去了,最后一个点999ms
#include <bits/stdc++.h> using namespace std; int block,ans=0,cnt[1000001]; int n,m,a[50010],Ans[200010]; struct node { int l,r,id; }q[200010]; bool cmp(node a,node b){ return (a.r/block)==(b.r/block)?a.l<b.l:a.r<b.r; } void add(int x){ if(!cnt[a[x]])ans++; cnt[a[x]]++; } void del(int x){ cnt[a[x]]--; if(!cnt[a[x]])ans--; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); block=sqrt(n); scanf("%d",&m); for(int i=1;i<=m;i++){ scanf("%d%d",&q[i].l,&q[i].r); q[i].id=i; } sort(q+1,q+m+1,cmp); int l=0,r=0; for(int i=1;i<=m;i++){ int ql=q[i].l,qr=q[i].r; while(l<ql)del(l++); while(l>ql)add(--l); while(r<qr)add(++r); while(r>qr)del(r--); Ans[q[i].id]=ans; } for(int i=1;i<=m;i++)printf("%d\n",Ans[i]); return 0; }
#include <bits/stdc++.h> using namespace std; int read() { char x; while((x = getchar()) > '9' || x < '0') ; int u = x - '0'; while((x = getchar()) <= '9' && x >= '0') u = (u << 3) + (u << 1) + x - '0'; return u; } int buf[105]; inline void write(int i) { int p = 0; if(i == 0) p++; else while(i) { buf[p++] = i % 10; i /= 10; } for(int j = p-1; j >= 0; j--) putchar('0' + buf[j]); } #define il inline #define re register int block,ans=0,cnt[1000001]; int n,m,a[500010],Ans[500010]; struct node { int l,r,id; }q[500010]; il bool cmp(node a,node b){ return (a.l/block)^(b.l/block)?a.l<b.l:(((a.l/block)&1)?a.r<b.r:a.r>b.r); } il void add(int x){ if(!cnt[a[x]])ans++; cnt[a[x]]++; } il void del(int x){ cnt[a[x]]--; if(!cnt[a[x]])ans--; } int main(){ n=read(); for(re int i=1;i<=n;i++)a[i]=read(); m=read(); block=n/sqrt(m*2/3); for(re int i=1;i<=m;i++){ q[i].l=read(),q[i].r=read(); q[i].id=i; } sort(q+1,q+m+1,cmp); re int l=0,r=0; for(re int i=1;i<=m;i++){ re int ql=q[i].l,qr=q[i].r; while(l<ql)del(l++); while(l>ql)add(--l); while(r<qr)add(++r); while(r>qr)del(r--); Ans[q[i].id]=ans; } for(re int i=1;i<=m;i++)write(Ans[i]),puts(""); return 0; }