题意:给你一个数组,然后每次有两种操作,操作一是修改数组里的数,操作二是查询区间[ l , r ] 里有多少个子区间满足以下条件:1、子区间内的数全部相同。2、子区间内的数在x到y之间。3、子区间得是不能延伸的。
题目链接:https://nanti.jisuanke.com/t/41356
题解:首先转化问题,设 b[ i ] = a[i]==a[i-1] ? 0 : a[i],然后问题就变成了问询区间内有多少个x到y之间的数。(注意左端点特判)这不就是主席树。。。。带修改。。。好,树状数组加主席树。。。然后题解来一句,卡树套树。正解cdq。虽然分块+树状数组可以过,但是cdq常数真的十分优秀。所以这题用了cdq和分块两种解法来写。
cdq:867ms(用c++11居然791ms??),分块:3027ms
首先分块:分成若干个块,每一个块就是一个树状数组,修改操作直接在该块树状数组修改。查询的话,直接每一个块的树状数组查询,思路很好想,实现也好写。
代码如下:
1 /************************************************************************* 2 > File Name: nanchangI.cpp 3 # File Name: nanchangI.cpp 4 # Author : xiaobuxie 5 # QQ : 760427180 6 # Email:[email protected] 7 # Created Time: 2019年09月09日 星期一 22时10分02秒 8 ************************************************************************/ 9 10 #include<iostream> 11 #include<cstdio> 12 #include<map> 13 #include<cmath> 14 #include<cstring> 15 #include<set> 16 #include<queue> 17 #include<vector> 18 #include<algorithm> 19 using namespace std; 20 typedef long long ll; 21 #define inf 0x3f3f3f3f 22 #define pq priority_queue<int,vector<int>,greater<int> > 23 ll gcd(ll a,ll b){ 24 if(a<b) return gcd(b,a); 25 return b==0?a:gcd(b,a%b); 26 } 27 28 const int N=2e5+9; 29 const int M=sqrt(N/(log(N)))+9; 30 int a[N],b[N],id[N]; 31 int tr[M][N]; 32 int n,m; 33 void add(int p,int x,int v){ for(;x<=n;x+=x&(-x)) tr[p][x]+=v;} 34 int sum(int p,int x){ 35 int res=0; 36 for(;x;x-=x&(-x)) res+=tr[p][x]; 37 return res; 38 } 39 int main(){ 40 scanf("%d %d",&n,&m); 41 //int block=sqrt(n*log(n)/log(2)); 42 int block=sqrt(n*log(n+1)); 43 for(int i=1;i<=n;++i) scanf("%d",a+i); 44 b[1]=a[1]; 45 for(int i=2;i<=n;++i) b[i]= a[i]==a[i-1] ? 0 : a[i]; 46 int cnt=1; 47 for(int i=1;i<=n;){ 48 for(int j=0;j<block && i+j<=n;++j) id[i+j]=cnt; 49 ++cnt; i+=block; 50 } 51 for(int i=1;i<=n;++i) if(b[i]) add(id[i],b[i],1); 52 for(int i=1;i<=m;++i){ 53 int opt; scanf("%d",&opt); 54 if(opt==1){ 55 int p,v;scanf("%d %d",&p,&v); 56 if(b[p]) add(id[p],b[p],-1); 57 if(p==1){ 58 add(id[p],v,1); 59 b[p]=a[p]=v; 60 } 61 else if(v!=a[p-1]) add(id[p],v,1),b[p]=a[p]=v; 62 else b[p]=0,a[p]=v; 63 64 if(p!=n){ 65 add(id[p+1],b[p+1],-1); 66 if(a[p]!=a[p+1]) add(id[p+1],a[p+1],1),b[p+1]=a[p+1]; 67 else b[p+1]=0; 68 } 69 70 } 71 else{ 72 int l,r,x,y; scanf("%d %d %d %d",&l,&r,&x,&y); 73 if(l==r){ 74 if(a[l]>=x && a[l]<=y) puts("1"); 75 else puts("0"); 76 continue; 77 } 78 ++l; 79 int ans=0; 80 int le=l,ri=r; 81 //cerr<<id[le]<<‘ ‘<<id[ri]<<endl; 82 if(id[le]==id[ri] || id[le]==id[ri]-1){ 83 for(int i=le;i<=ri;++i){ 84 //cerr<<i<<‘ ‘<<b[i]<<endl; 85 if(b[i]>=x && b[i]<=y) ++ans; 86 } 87 } 88 89 else{ 90 for(;id[le]==id[l];++le) if(b[le]>=x && b[le]<=y) ++ans; 91 for(;id[ri]==id[r];--ri) if(b[ri]>=x && b[ri]<=y) ++ans; 92 for(int i=id[le];i<=id[ri];++i) ans+=sum(i,y)-sum(i,x-1); 93 } 94 //cerr<<ans<<endl; 95 if(a[l-1]>=x && a[l-1]<=y) ++ans; 96 printf("%d\n",ans); 97 } 98 } 99 return 0; 100 }
然后说说正解cdq吧:首先我们可以把询问拆成两个操作,对[ 1, l-1 ]和 [ 1, r ] 询问x到y之间有多少数。然后每个操作有三个属性:id(哪一位),时间,ty(类型),所以就隐隐约约看到三维偏序问题。可以这样看,每次询问操作,就是看id比他小的,并且时间比他小的x到y的数多少个。一开始读入就是按照时间来读入的(第一维)。然后对id进行分治(第二维),然后注意cdq中经常注意的一点:左区间的修改影响右区间的查询,也就是说左指针操作是修改才进行,右指针操作是询问才进行。每个询问操作要看他时间轴左边的,在同一个子区间的会分治时处理,不在同一个子区间的归并会处理,所以不重不漏。
最后记得清空树状数组。
然后好像说对结构体进行多次移动会变慢,所以这里写成了分开的了。开起来可能不大习惯。
1 /************************************************************************* 2 > File Name: nanchangeI.cpp 3 # File Name: nanchangeI.cpp 4 # Author : xiaobuxie 5 # QQ : 760427180 6 # Email:[email protected] 7 # Created Time: 2019年09月10日 星期二 20时05分47秒 8 ************************************************************************/ 9 10 #include<iostream> 11 #include<cstdio> 12 #include<map> 13 #include<cmath> 14 #include<cstring> 15 #include<set> 16 #include<queue> 17 #include<vector> 18 #include<algorithm> 19 using namespace std; 20 typedef long long ll; 21 #define inf 0x3f3f3f3f 22 #define pq priority_queue<int,vector<int>,greater<int> > 23 ll gcd(ll a,ll b){ 24 if(a<b) return gcd(b,a); 25 return b==0?a:gcd(b,a%b); 26 } 27 28 const int M=2e6+9; 29 const int N=2e5+8; 30 int id[M],ty[M],X[M],Y[M],ansid[M],val[M]; 31 int ans[N]; 32 int B[M],A[M],tr[N]; 33 int a[N],b[N]; 34 int n,m; 35 void add(int x,int v){ 36 for(;x<=n;x+=x&(-x)) tr[x]+=v; 37 } 38 int query(int x){ 39 int res=0; 40 for(;x;x-=x&(-x)) res+=tr[x]; 41 return res; 42 } 43 void cdq(int l,int r){ 44 if(l==r) return; 45 int m=(l+r)>>1; 46 cdq(l,m); cdq(m+1,r); 47 for(int i=l,t1=l,t2=m+1;i<=r;++i){ 48 if(t1<=m && (id[ A[t1] ] <= id[ A[t2] ] || t2>r)){ 49 B[i]=A[t1]; 50 if(ty[ A[t1] ]==1){ 51 if(val[ A[t1] ]>=0) add(val[ A[t1] ],1); 52 else add(-val[ A[t1] ],-1); 53 } 54 ++t1; 55 } 56 else{ 57 B[i]=A[t2]; 58 if(ty[ A[t2] ]==2){ 59 ans[ ansid[A[t2]] ]+=query(X[ A[t2] ]); 60 ans[ ansid[A[t2]] ]-=query(Y[ A[t2] ]); 61 } 62 if(ty[ A[t2] ]==3){ 63 ans[ ansid[A[t2]] ]-=query(X[ A[t2] ]); 64 ans[ ansid[A[t2]] ]+=query(Y[ A[t2] ]); 65 } 66 ++t2; 67 } 68 } 69 for(int i=l;i<=m;++i){ 70 if(ty[ A[i] ]==1){ 71 if(val[ A[i] ]>=0) add(val[ A[i] ],-1); 72 else add(-val[ A[i] ],1); 73 } 74 } 75 for(int i=l;i<=r;++i) A[i]=B[i]; 76 } 77 int main(){ 78 scanf("%d %d",&n,&m); 79 int tot=0; 80 for(int i=1;i<=n;++i) scanf("%d",a+i); 81 b[1]=a[1]; 82 for(int i=2;i<=n;++i) b[i] = a[i]==a[i-1] ? 0 : a[i]; 83 for(int i=1;i<=n;++i){ 84 if(!b[i]) continue; 85 id[++tot]=i; ty[tot]=1; val[tot]=b[i]; 86 } 87 int cnt=0; 88 for(int i=1;i<=m;++i){ 89 int opt; scanf("%d",&opt); 90 if(opt==1){ 91 int p,v; scanf("%d %d",&p,&v); 92 if(b[p]) id[++tot]=p; val[tot]=-b[p]; ty[tot]=1; 93 if(p==1){ 94 id[++tot]=p; val[tot]=v; ty[tot]=1; 95 b[p]=a[p]=v; 96 } 97 else{ 98 if(v!=a[p-1]){ 99 id[++tot]=p; val[tot]=v; ty[tot]=1; 100 a[p]=b[p]=v; 101 } 102 else a[p]=v,b[p]=0; 103 } 104 105 if(p!=n){ 106 if(b[p+1]) id[++tot]=p+1; val[tot]=-b[p+1]; ty[tot]=1; 107 if(a[p]!=a[p+1]){ 108 b[p+1]=a[p+1]; 109 id[++tot]=p+1; val[tot]=b[p+1]; ty[tot]=1; 110 } 111 else b[p+1]=0; 112 } 113 } 114 else{ 115 int l,r,x,y; scanf("%d %d %d %d",&l,&r,&x,&y); 116 if(l==r){ 117 if(a[l]>=x && a[l]<=y) ans[++cnt]=1; 118 else ans[++cnt]=0; 119 continue; 120 } 121 ++l; ++cnt; 122 ty[++tot]=2; id[tot]=l-1; X[tot]=x-1; Y[tot]=y; ansid[tot]=cnt; 123 ty[++tot]=3; id[tot]=r; X[tot]=x-1; Y[tot]=y; ansid[tot]=cnt; 124 if(a[l-1]>=x && a[l-1]<=y) ans[cnt]++; 125 } 126 } 127 for(int i=1;i<=tot;++i) A[i]=i; 128 cdq(1,tot); 129 for(int i=1;i<=cnt;++i) printf("%d\n",ans[i]); 130 return 0; 131 }
原文地址:https://www.cnblogs.com/xiaobuxie/p/11503509.html