码迷,mamicode.com
首页 > 其他好文 > 详细

2019 南昌网络赛icpc I题 cdq分治或分块

时间:2019-09-10 23:38:03      阅读:123      评论:0      收藏:0      [点我收藏+]

标签:const   ace   color   lse   add   时间轴   个数   nan   延伸   

题意:给你一个数组,然后每次有两种操作,操作一是修改数组里的数,操作二是查询区间[ 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:760427180@qq.com
  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 }
View Code

 

然后说说正解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:760427180@qq.com
  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 }
View Code

 

2019 南昌网络赛icpc I题 cdq分治或分块

标签:const   ace   color   lse   add   时间轴   个数   nan   延伸   

原文地址:https://www.cnblogs.com/xiaobuxie/p/11503509.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!