bzoj-2743 采花

来源:互联网 发布:桌面windows手机版 编辑:程序博客网 时间:2024/04/29 02:52

题意:

给出一个长度为n的序列,m次查询区间[l,r]中出现两次的数的种类数;

n,m<=1000000;


题解:

一开始看出来数据范围。。少看一个0;

于是写了一发莫队,O(n√n)直接TLE掉;

这题正解当然是O(nlogn)的算法咯,和HH的项链那道题的思想确实很相似;

我们记录与第i个数相同的最后一个数下标为pre[i];

将询问离线,按r端点排序,每扫到一个点i就回答r端点为i的所有询问;

扫过i时,我们在树状数组中将pre[i]的值+1,pre[pre[i]]的值-1;

这时,对于一个区间[l,r]来说,直接查询[l,r]的区间和吗是答案咯;

并不需要区间+1什么的操作呢,况且树状数组维护那东西我又不会= =;

时间复杂度O(nlogn),代码复杂度也是很低的;


代码:


#include<cctype>#include<stdio.h>#include<string.h>#include<algorithm>#define N 1000010#define LEN 1<<16using namespace std;struct node{int l,r,no;friend bool operator<(node a,node b){return a.r<b.r;} }Q[N];int a[N],pre[N],last[N],sum[N],ans[N],n;char getc(){    static char *S,*T,buf[LEN];    if(S==T)    {        T=(S=buf)+fread(buf,1,LEN,stdin);        if(S==T)            return EOF;    }    return *S++;}int read(){    static char ch;    static int D;    while(!isdigit(ch=getc()));    for(D=ch-'0';isdigit(ch=getc());)        D=D*10+ch-'0';    return D;}int lowbit(int x){return x&(-x);}void update(int x,int val){if(!x)return ;while(x<=n){sum[x]+=val;x+=lowbit(x);}}int query(int x){int ret=0;while(x){ret+=sum[x];x-=lowbit(x);}return ret;}int main(){int c,m,i,j,k,l,r;n=read(),c=read(),m=read();for(i=1;i<=n;i++)a[i]=read(),pre[i]=last[a[i]],last[a[i]]=i;for(i=1;i<=m;i++){Q[i].l=read(),Q[i].r=read();Q[i].no=i;}sort(Q+1,Q+m+1);for(i=1,j=1;i<=n;i++){update(pre[i],1);update(pre[pre[i]],-1);while(j<=m&&Q[j].r==i){ans[Q[j].no]=query(i)-query(Q[j].l-1);j++;}}for(i=1;i<=m;i++)printf("%d\n",ans[i]);return 0;}



0 0
原创粉丝点击