【题目描述】
萧芸斓是Z 国的公主,平时的一大爱好是采花。
今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花。花园足够大,容纳
了n 朵花,花有c 种颜色(用整数1-c 表示),且花是排成一排的,以便于公主采花。
公主每次采花后会统计采到的花的颜色数,颜色数越多她会越高兴!同时,她有一癖好,
她不允许最后自己采到的花中,某一颜色的花只有一朵。为此,公主每采一朵花,要么此前
已采到此颜色的花,要么有相当正确的直觉告诉她,她必能再次采到此颜色的花。
由于时间关系,公主只能走过花园连续的一段进行采花,便让女仆福涵洁安排行程。福
涵洁综合各种因素拟定了m 个行程,然后一一向你询问公主能采到多少朵花(她知道你是编
程高手,定能快速给出答案!),最后会选择令公主最高兴的行程(为了拿到更多奖金!)。
【输入格式】
第一行四个空格隔开的整数n、c 以及m。
接下来一行n 个空格隔开的整数,每个数在[1, c]间,第i 个数表示第i 朵花的颜色。
接下来m 行每行两个空格隔开的整数l 和r(l ≤ r),表示女仆安排的行程为公主经
过第l 到第r 朵花进行采花。
【输出格式】
共m 行,每行一个整数,第i 个数表示公主在女仆的第i 个行程中能采到的花的颜色数。
【输入样例】
5 3 5
1 2 2 3 1
1 5
1 2
2 2
2 3
3 5
【输出样例】
2
0
0
1
0
【样例说明】
询问[1, 5]:公主采颜色为1 和2 的花,由于颜色3 的花只有一朵,公主不采;
询问[1, 2]:颜色1 和颜色2 的花均只有一朵,公主不采;
询问[2, 2]:颜色2 的花只有一朵,公主不采;
询问[2, 3]:由于颜色2 的花有两朵,公主采颜色2 的花;
询问[3, 5]:颜色1、2、3 的花各一朵,公主不采。
【数据范围】
n,m,c<=10^6
这道题考试的时候是小数据所以打了莫队,非常好理解。
但原题数据大10倍,莫队只有60分,所以用到另外一种实用的离线处理方式,可以参照8.4日考试时HH的项链这道题,思路比较相似。先补充一下树状数组区域更新的方式,比如说使L->R区间内所有位置加T,则在L+1,R+1处-1,不理解可以画画图,显然正确。
然后是这道题,现将所有操作读入进来,按右端点升序排列,记录pre[i]为i位置上的值上一次出现的位置,i-向右扫描,每次扫描一位就处理一次,如果有操作的右端点与其重合,则查询。如何处理,我们知道,pre[i]->i之间这段i中i不能算,但是pre[pre[i]]->pre[i]
之间的之前不行,但现在可以算1种了,在这段上整体+1,如此处理,之间树状数组Ask一下sum[op[i].L]即可,正确性可画图理解一下,显然。
#define MAXN 1000010UL #include<cstdio> #include<algorithm> using namespace std; int n,m,c,q[MAXN],pre[MAXN],sd[MAXN],p1,p2; int Ans[MAXN],now,sum[MAXN]; struct Node{ int l,r,id; }op[MAXN]; bool cop(const Node a,const Node b){ return a.r==b.r?(a.l<b.l):(a.r<b.r); } int lowbit(int x){ return x&(-x); } void Update(int x,int t){ while(x<=n&&x!=0){ sum[x]+=t; x+=lowbit(x); } } int Ask(int x){ int fec=0; while(x>0){ fec+=sum[x]; x-=lowbit(x); } return fec; } int main(){ freopen("flower.in","r",stdin); freopen("flower.out","w",stdout); scanf("%d%d%d",&n,&c,&m); for(int i=1;i<=n;i++){ scanf("%d",&q[i]); pre[i]=sd[q[i]]; sd[q[i]]=i; } for(int i=1;i<=m;i++){ op[i].id=i; scanf("%d%d",&op[i].l,&op[i].r); } sort(op+1,op+m+1,cop); now=1; for(int i=1;i<=n;i++){ p1=pre[i]; p2=pre[p1]; Update(p2+1,1); Update(p1+1,-1); while(op[now].r==i){ Ans[op[now].id]=Ask(op[now].l); now++; } if(now>m) break; } for(int i=1;i<=m;i++) printf("%d\n",Ans[i]); getchar(); getchar(); }
附上莫队算法的:
#define MAXN 100100UL #include<cstdio> #include<cmath> #include<algorithm> using namespace std; int n,m,c[MAXN],k,L,R,s[MAXN],ans,Q[MAXN],bl[MAXN],blocks; struct Node{ int id,l,r; }op[MAXN]; int cop(const Node a,const Node b){ if(bl[a.l]==bl[b.l]) return a.r<b.r; else return a.l<b.l; } void Update(int x,int t){ if(t==1){ if(s[c[x]]==1) ans++; } if(t==-1){ if(s[c[x]]==2) ans--; } s[c[x]]+=t; } int main(){ freopen("flower.in","r",stdin); freopen("flower.ans","w",stdout); scanf("%d%d%d",&n,&k,&m); blocks=(int)sqrt(n+0.5); for(int i=1;i<=n;i++){ scanf("%d",&c[i]); bl[i]=(i-1)/blocks+1; } for(int i=1;i<=m;i++){ scanf("%d%d",&op[i].l,&op[i].r); op[i].id=i; } sort(op+1,op+m+1,cop); L=op[1].l; R=op[1].r; for(int i=L;i<=R;i++){ Update(i,1); } Q[op[1].id]=ans; for(int i=2;i<=m;i++){ while(L<op[i].l){ Update(L,-1); L++; } while(L>op[i].l){ L--; Update(L,1); } while(R<op[i].r){ R++; Update(R,1); } while(R>op[i].r){ Update(R,-1); R--; } Q[op[i].id]=ans; } for(int i=1;i<=m;i++){ printf("%d\n",Q[i]); } getchar(); getchar(); getchar(); }