题目链接
考虑把每种颜色的贡献放到一个位置来算,当然,要保证该颜色至少出现两次。
发现无论是扫描左端点并在区间第一次出现时取贡献,还是扫描右端点并在区间最后一次出现时取贡献,都没办法用树状数组很方便地维护,因为我们是取区间和来当做答案的,也就是对于某颜色来说,关于固定端点,不固定的那个端点必须比该颜色第二远的出现位置更远。考虑放在区间里第二次出现某种颜色的花的位置来算,那么$(1,2,3,2,3,4,1,1)$就会转化成$(0,1,1,0,0,0,1,0)$(询问区间的左端点是$1$或$2$时),不断扫描过左端点,利用树状数组动态维护前缀和,每次询问右端点即可。
考虑把询问按照区间左端点升序排序,接下来看怎么在区间左端点移动的情况下维护树状数组,以便我们询问右端点作为答案。
设$pre_i$,$suc_i$分别表示$i$号花同颜色的前驱和后继。
首先我们要理解:我们的当前左端点$l$是正在两个排序好的询问区间的左端点间移动的,也就是说,$l$号花是不能计入答案,也不能算进$x_l$的个数的,同时它可以滚动更新。
那么我们要保证一朵花至少出现两次,它的位置就是$suc[\,suc[l]\,]$(如果存在的话)。同时,既然我们在$suc[\,suc[l]\,]$处加一了,就必须在$suc[l]$处减一(如果存在的话),因为$suc[l]$也做过$pre[l]$的二级后继,也即$suc[l]=suc[\,suc[\,pre[l]\,]\,]$时,$suc[l]$处是有加过一的。当然,我们要先把全局第二次出现的花的位置加一,因为它们没有二级前驱来给自己加一。注意:$l$只能移动到下一个询问区间的左端点减一。
还有一点,就是本题开了两档数据,第二档是$2 imes 10^6$的规模,也就是卡常了。我这里用的是树状数组,用了快读快写和register才过。这里放几个截图。
无常数优化:
加入快读和register:
再加入快写:
代码(100分):
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<queue> #include<map> #include<set> #define IL inline #define RG register #define RI RG int #define _1 first #define _2 second using namespace std; const int N=2e6; IL void read(RI &x){ x=0; RG char ch=getchar(); while(!isdigit(ch)) ch=getchar(); for(;isdigit(ch);ch=getchar()) x=(x<<3)+(x<<1)+ch-'0'; } IL void write(RI x){ RI k=0,s[13]; if(x) for(;x;x/=10) s[++k]=x%10; else s[++k]=0; while(k) putchar(s[k--]+'0'); } IL void writeln(RI x){ write(x); putchar(' '); } int n,c,m,x[N+3]; struct Qry{ int l,r,d; }a[N+3]; IL bool operator<(RG Qry x,RG Qry y){ return x.l<y.l; } int s[N+3]; IL int lowbit(RI x){ return x&(-x); } IL void mdf(RI p,RI x){ for(;p<=n;p+=lowbit(p)) s[p]+=x; } IL int qry(RI p){ RI ret=0; for(;p;p-=lowbit(p)) ret+=s[p]; return ret; } int pre[N+3],suc[N+3],col[N+3]; IL void init(){ for(RI i=1;i<=n;i++){ if(col[x[i]]) pre[i]=col[x[i]]; col[x[i]]=i; } memset(col,0,sizeof col); for(RI i=n;i>=1;i--){ if(col[x[i]]) suc[i]=col[x[i]]; col[x[i]]=i; } for(RI i=1;i<=n;i++) if(!pre[i]&&suc[i]) mdf(suc[i],1); sort(a+1,a+m+1); } int ans[N+3]; int main(){ read(n); read(c); read(m); for(RI i=1;i<=n;i++) read(x[i]); for(RI i=1;i<=m;i++){ read(a[i].l); read(a[i].r); a[i].d=i; } init(); for(RI i=1;i<=m;i++){ for(RI j=a[i-1].l;j<a[i].l;j++) if(suc[j]){ mdf(suc[j],-1); if(suc[suc[j]]) mdf(suc[suc[j]],1); } ans[a[i].d]=qry(a[i].r); } for(RI i=1;i<=m;i++) writeln(ans[i]); return 0; }