CF848C:Goodbye Souvenir(CDQ分治)
Description
给定长度为$n$的数组, 定义数字$X$在$[l,r]$内的值为数字$X$在$[l,r]$内最后一次出现位置的下标减去第一次出现位置的下标
给定$m$次询问, 每次询问有三个整数$a,b,c$询问规则如下:
当$a=1$时, 将数组内第$b$个元素更改为$c$
当$a=2$时, 求区间$[b,c]$所有数字的值的和
Input
第一行两个整数$n$,$m$
第二行$n$个整数, 表示数组
第$3$到$3+m$行, 每行三个整数, 表示每次询问。
Output
对于每次$a=2$的询问, 输出一个整数表示答案
Sample Input1
7 6
1 2 3 1 3 2 1
2 3 7
2 1 3
1 7 2
1 3 2
2 1 6
2 5 7
Sample Output1
5
0
7
1
Sample Input2
7 5
1 3 2 1 4 2 3
1 1 4
2 2 3
1 1 7
2 4 5
1 1 7
Sample Output2
0
0
Solution
设初始每个位置对应点$(i,pre[i])$,权值为$i-pre[i]$。可以把初始位置上的点看成矩形单点加操作。
$pre[i]$为$i$这个位置的数上一次出现的位置,若没有则为$0$。
那么查询区间$[L,R]$就相当于查询左下$(L,L)$右上$(R,R)$的矩形的权值和(写写画画可能比较容易明白),可以$CDQ$。
考虑一次修改会影响什么?设$i$位置把$x$修改成$y$,只会影响和$i$相邻的$x$和$y$,这个可以用$set$维护,然后看成若干矩形单点加操作。
那么就可以写一个只有单点加和矩形求和的$CDQ$分治了。
Code
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<set> 5 #define N (700009) 6 #define LL long long 7 using namespace std; 8 9 struct Que{int x,y,opt,v;}Q[N],tmp[N]; 10 int n,m,cnt,q_num; 11 int a[N],b[N],pre[N]; 12 LL c[N],ans[N]; 13 set<int>S[N]; 14 set<int>::iterator it; 15 16 inline int read() 17 { 18 int x=0,w=1; char c=getchar(); 19 while (c<'0' || c>'9') {if (c=='-') w=-1; c=getchar();} 20 while (c>='0' && c<='9') x=x*10+c-'0', c=getchar(); 21 return x*w; 22 } 23 24 void Update(int x,int k) 25 { 26 for (; x<=n+1; x+=(x&-x)) c[x]+=k; 27 } 28 29 LL Query(int x) 30 { 31 LL ans=0; 32 for (; x; x-=(x&-x)) ans+=c[x]; 33 return ans; 34 } 35 36 void CDQ(int l,int r) 37 { 38 if (l==r) return; 39 int mid=(l+r)>>1; 40 CDQ(l,mid); CDQ(mid+1,r); 41 int i=l,j=mid+1,k=l-1; 42 while (i<=mid || j<=r) 43 if (j>r || i<=mid && (Q[i].x<Q[j].x || Q[i].x==Q[j].x && Q[i].opt<Q[j].opt)) 44 { 45 if (Q[i].opt==1) Update(Q[i].y,Q[i].v); 46 tmp[++k]=Q[i]; ++i; 47 } 48 else 49 { 50 if (Q[j].opt==2) 51 { 52 if (Q[j].v>0) ans[Q[j].v]+=Query(Q[j].y); 53 else ans[-Q[j].v]-=Query(Q[j].y); 54 } 55 tmp[++k]=Q[j]; ++j; 56 } 57 for (int i=l; i<=mid; ++i) 58 if (Q[i].opt==1) Update(Q[i].y,-Q[i].v); 59 for (int i=l; i<=r; ++i) Q[i]=tmp[i]; 60 } 61 int main() 62 { 63 n=read(); m=read(); 64 for (int i=1; i<=n; ++i) 65 { 66 a[i]=read(); pre[i]=b[a[i]]; b[a[i]]=i; 67 S[a[i]].insert(i); Q[++q_num]=(Que){i,pre[i],1,i-pre[i]}; 68 } 69 for (int i=1; i<=m; ++i) 70 { 71 int opt=read(),x=read(),y=read(); 72 if (opt==1) 73 { 74 int p1=0,n1=0;//前驱 后继 75 it=S[a[x]].find(x); 76 if (it!=S[a[x]].begin()) --it, p1=*it, ++it; 77 if ((++it)!=S[a[x]].end()) n1=*it; --it; 78 S[a[x]].erase(*it); Q[++q_num]=(Que){x,pre[x],1,pre[x]-x}; 79 if (n1) 80 { 81 Q[++q_num]=(Que){n1,pre[n1],1,pre[n1]-n1}; 82 pre[n1]=p1; 83 Q[++q_num]=(Que){n1,pre[n1],1,n1-pre[n1]}; 84 } 85 86 int p2=0,n2=0; 87 a[x]=y; S[a[x]].insert(x); 88 it=S[a[x]].find(x); 89 if (it!=S[a[x]].begin()) --it, p2=*it, ++it; 90 if ((++it)!=S[a[x]].end()) n2=*it; --it; 91 pre[x]=p2; Q[++q_num]=(Que){x,pre[x],1,x-pre[x]}; 92 if (n2) 93 { 94 Q[++q_num]=(Que){n2,pre[n2],1,pre[n2]-n2}; 95 pre[n2]=x; 96 Q[++q_num]=(Que){n2,pre[n2],1,n2-pre[n2]}; 97 } 98 } 99 else 100 { 101 ++cnt; 102 Q[++q_num]=(Que){x-1,x-1,2,cnt}; 103 Q[++q_num]=(Que){y,y,2,cnt}; 104 Q[++q_num]=(Que){x-1,y,2,-cnt}; 105 Q[++q_num]=(Que){y,x-1,2,-cnt}; 106 } 107 } 108 for (int i=1; i<=q_num; ++i) Q[i].x++, Q[i].y++; 109 CDQ(1,q_num); 110 for (int i=1; i<=cnt; ++i) printf("%lld\n",ans[i]); 111 }