Codeforces1198B Welfare State(分块)
题目链接:http://codeforces.com/problemset/problem/1198/B
题目大意:n个数据,q个操作,每个操作有两种情况,一种是将某一个数据改为x,另一种是将所有小于x的数据改为x。所有操作结束后输出数组。
这道题是我第一次在非练习(就是那种半天一题磨洋工状态)情况下用分块解决了题目,所以写篇详解纪念一下。
这道题大佬们的写法貌似不是分块emmmm,也对,根据题目输出的特殊性或许有别的办法,但蒟蒻我目前能想到的只有分块了。(莽过了就好)
思路就是用一个mi数组记录每个块的最小值,一个mark数组记录该块是否被修改过(详细解释见下)。
显然的,对于第二种操作,如果某个块的最小值大于x,那我们就不用对这个块进行操作。反之,我们把这个块的最小值标为x,并且让这个块的mark置1。然后,对于mark置1的块,如果我们的操作1访问到它了,就遍历一次块,将每个<=x的数改为x,再进行修改。
输出的时候只要注意判断一下mark的状态就行了。最坏的情况两种修改的复杂度都是O根号n。
1 #include<iostream> 2 #include<cstdio> 3 #include<set> 4 #include<stdio.h> 5 #include<string.h> 6 #include<math.h> 7 #include<vector> 8 #include<stdlib.h> 9 #include<queue> 10 #include<algorithm> 11 #include<map> 12 #include<stack> 13 using namespace std; 14 int a[200005]; 15 int bl[200005]; 16 int blo; 17 int mi[2005];//每个块的最小值 18 int mark[2005];//标记状态 19 int le[2005];//每个块的左边界 20 int ri[2005];//每个块的右边界 21 int main() 22 { 23 int n; 24 scanf("%d",&n); 25 blo=sqrt(n); 26 memset(mi,-1,sizeof(mi)); 27 for(int i=1;i<=n;i++) 28 { 29 scanf("%d",&a[i]); 30 bl[i]=(i-1)/blo+1; 31 if(mi[bl[i]]==-1||a[i]<mi[bl[i]]) 32 { 33 mi[bl[i]]=a[i]; 34 } 35 if(le[bl[i]]==0) 36 { 37 le[bl[i]]=i; 38 } 39 ri[bl[i]]=i; 40 } 41 int q; 42 memset(mark,-1,sizeof(mark)); 43 scanf("%d",&q); 44 int k,p,x; 45 while(q--) 46 { 47 scanf("%d",&k); 48 if(k==1) 49 { 50 scanf("%d%d",&p,&x); 51 if(mark[bl[p]]==1) 52 { 53 for(int i=le[bl[p]];i<=ri[bl[p]];i++) 54 { 55 if(a[i]<mi[bl[p]]) 56 { 57 a[i]=mi[bl[p]]; 58 } 59 } 60 mark[bl[p]]=-1; 61 } 62 a[p]=x; 63 if(x<mi[bl[p]]) 64 { 65 mi[bl[p]]=x; 66 } 67 } 68 else 69 { 70 scanf("%d",&x); 71 for(int i=1;i<=bl[n];i++) 72 { 73 if(mi[i]>=x) 74 { 75 continue; 76 } 77 else 78 { 79 mi[i]=x; 80 mark[i]=1; 81 } 82 } 83 } 84 } 85 for(int i=1;i<=n;i++) 86 { 87 if(mark[bl[i]]==-1||a[i]>=mi[bl[i]]) 88 { 89 printf("%d%c",a[i],i==n?'\n':' '); 90 } 91 else 92 { 93 printf("%d%c",mi[bl[i]],i==n?'\n':' '); 94 } 95 } 96 return 0; 97 }