[HEOI2012]采花

[HEOI2012]采花

[洛谷链接](https://www.luogu.org/problemnew/show/P4113)

  这道题第一眼看是一道莫队的简单题,提交后非常愉快地TLE了。简单的分析后,发现依然是一道简单题。

## Solution

  看到这道题不难想的用树状数组+离线来解决这道题。先将所有的查询按\(l\)升序排序。显然一个位置产生贡献只有从起点到当前位置第二次出现于当前位置相同颜色,我们用树状数组来维护这些位置对结果产生的贡献。由于\(l\)之前已经排好序了,所以每次操作只要将\(l_{i-1}\)\(l_{i}\)之间所有的位置上的颜色产生贡献的位置向后偏移一位,即对于一个颜色\(c_i\)撤销下次出现此颜色的位置的贡献,然后找到下下次出现这种颜色的位置,记上它的贡献,在用树状数组查询答案。然而直接做貌似是\(O(n^2)\),不难想到我们可以用一个链表来查找每一个位置下一次出现与其颜色相同的位置,显然的复杂度就只有\(O(nlog_2n)\)了,然而n小于等于\(2e6\),注意一些常数优化即可。

  


#include <cstdio>
#include <cmath>
#include <algorithm>
#define N 2000005
#define ll long long 
#define rint register int
using namespace std;
inline char get(){
    const int TOP=1000000;
    static char T[TOP],*x=T,*y=T;
    return x==y&&(y=(x=T)+fread(T,1,TOP,stdin),x==y)?EOF:*x++;
}
inline int read (){
    register int num,sign=1;register char c;
    while (((c=get())<'0'||c>'9')&&c!='-');c=='-'?num=sign=0:num=c-48;
    while ((c=get())>='0'&&c<='9')num=(num<<3)+(num<<1)+(c-48);
    return sign?num:-num;
}
struct node{int l,r,id;bool operator <(const node &x)const{return l<x.l;};}q[N];
int n,c,m;
int tr[N],next[N],A[N],head[N],id[N],vis[N],ans[N];
inline void M(rint x,rint data){if(x==0)return;while(x<=n){tr[x]+=data;x+=x&-x;}}
inline int Q(rint x){rint res=0;while(x){res+=tr[x];x-=x&-x;}return res;}
int main(){
	n=read();c=read();m=read();
	for(rint i=1;i<=n;++i){++vis[A[i]=read()];if(vis[A[i]]==2)id[A[i]]=i,M(i,1);next[head[A[i]]]=i;head[A[i]]=i;}
	for(rint i=1;i<=m;++i){q[i].id=i;q[i].l=read();q[i].r=read();}sort(q+1,q+1+m);next[0]=0; 
	for(rint i=1,j=1;i<=m;++i){
		while(j<q[i].l){M(id[A[j]],-1);M(id[A[j]]=next[id[A[j]]],1);++j;}
		ans[q[i].id]=Q(q[i].r);
	}
	for(rint i=1;i<=m;++i)printf("%d\n",ans[i]);
}
posted @ 2019-03-02 00:34  利姆鲁·特恩佩斯特  阅读(94)  评论(0)    收藏  举报