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

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

时间: 2024-07-31 11:03:25

2019 南昌网络赛icpc I题 cdq分治或分块的相关文章

ACM-ICPC 2019南昌网络赛I题 Yukino With Subinterval

ACM-ICPC 2019南昌网络赛I题 Yukino With Subinterval 题目大意:给一个长度为n,值域为[1, n]的序列{a},要求支持m次操作: 单点修改 1 pos val 询问子区间中某个值域的数的个数,连续的相同数字只记为一个.(即统计数字段的个数) 2 L R x y 数据范围: 1 ≤ n,m ≤ 2×10^5 1 ≤ a[i] ≤ n 解题思路: 连续重复的数字只记一次.所以考虑将每个数字段除第一个出现外的数字都删去(记为0).在读入操作的时候暴力模拟,同时维护

2019南昌网络赛-I(单调栈+线段树)

题目链接:https://nanti.jisuanke.com/t/38228 题意:定义一段区间的值为该区间的和×该区间的最小值,求给定数组的最大的区间值. 思路:比赛时还不会线段树,和队友在这题上弄了3小时,思路大体都是对的,但就是没法实现.这几天恶补线段树. 首先可以利用单调栈来查找满足a[i]为最小值的最大区间L[i]~R[i].然后利用线段树求一个段的和sum.最小前缀lsum和最小后缀rsum.然后遍历a[i]: a[i]>0:最优为sum(L[i],R[i])*a[i] a[i]<

2019南昌网络赛-I. Yukino With Subinterval 线段树套树状数组

TMD...这题卡内存卡的真优秀... 所以以后还是别用主席树的写法...不然怎么死的都不知道... 树套树中,主席树方法开权值线段树...会造成空间的浪费...这道题内存卡的很紧... 由于树套树已经不需要持久化了,直接动态开点就完事了...用主席树方法开过不去,要么超内存,要么越界... 大概思路...这题要求的[L,R]区间内,满足x<=a[i]<=y的连续的段数, 这题大概是个套路题,我们很容易想到,我们把连续的区间变成单点,把一段区间,类似1 1 1 3 5 变成 1 0 0 3 5

2019 CCPC 网络赛第三题 K-th occurrence 后缀数组+划分树+ST表+二分

题意:给你一个长度为n的字符串,每次询问给出三个数:L , R , K,表示原串 L 到 R 的子串在原串第K次出现的首字母的位置 解题思路:对子串的大量操作,不难想到后缀数组(后缀树/后缀自动机不会,所以没想到),注意到子串s[L.....R]必然是某一个后缀的前缀,所以所有前缀是该子串的后缀的排名(即rank数组的值)必定连续,也就是说在后缀数组(sa数组)中,下标是连续的,那么就是求区间第K大了(因为sa数组的值代表的是在字符串中的位置)(这里区间第K大我用划分树求),至于这一段区间的起点

2019 ICPC 南昌网络赛

2019 ICPC 南昌网络赛 比赛时间:2019.9.8 比赛链接:The 2019 Asia Nanchang First Round Online Programming Contest 总结 // 史上排名最高一次,开场不到两小时队友各A一题加水题共四题,排名瞬间升至三四十名 // 然后后三小时就自闭了,一题都没有突破...最后排名211 hhhh ? ? B. Fire-Fighting Hero 题意 队友做的,待补. ? AC代码 #include<cstdio> #includ

2019徐州网络赛 XKC&#39;s basketball team 线段树

网址:https://nanti.jisuanke.com/t/41387 题意: 大家好,我是训练时长两年半的个人练习生蔡徐坤,我的爱好是唱,跳,rap,篮球. 给出一段长度为$n,(n \leq 1e5)$的序列,对每一个数,求出它和它后面比它大$m$的数中间夹着的数的数量,没有输出$-1$. 题解: 直接建线段树,维护最大值,然后查询时对第$i$个数,搜索区间$[i,n]$之中大于$num[i]+m$的值的位置的最大值,具体操作是先限定区间,然后求出所有合法位置,取最大值,如果搜索不到则返

(未完结)“文远知行杯”GDET第十四届竞赛(网络赛共10题,仅整理出6题)

刚开学没多久就打了一个网络赛,通过这次网络赛我是发现我是真的菜... 放假前校赛的排名让我有些自满,寒假丝毫没有接触ACM,一直沉迷于Steam,这个真的值得好好反省. 虽然现在大一课有点多,在学校也有些事务,但是这些都不是我松懈的理由, 在此写下这篇博客就是为了提醒自己:Why(为什么别人进科协,我要打 ACM ),How,What 这次比赛的反思: 数论的学习实在是太过于薄弱,要加强,对数字的规律不够敏感,要锻炼, 数据结构最常用的树,不会,要学, 这次题目总体不算特别难,题目的灵活度不大,

2019 CCPC网络赛

一到网络赛,大家都是东亚之光 1001 00:23:46 solved by hl 签到 枚举一下位就行了 #include <map> #include <set> #include <ctime> #include <cmath> #include <queue> #include <stack> #include <vector> #include <string> #include <bitset

【BZOJ-1176&amp;2683】Mokia&amp;简单题 CDQ分治

1176: [Balkan2007]Mokia Time Limit: 30 Sec  Memory Limit: 162 MBSubmit: 1854  Solved: 821[Submit][Status][Discuss] Description 维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000. Input 第一行两个整数,S,W;其中S为矩阵初始值;W为矩阵大小