Codeforces 558E A Simple Task (计数排序&&线段树优化)
Posted kongbursi-2292702937
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces 558E A Simple Task (计数排序&&线段树优化)相关的知识,希望对你有一定的参考价值。
题目链接:http://codeforces.com/contest/558/problem/E
E. A Simple Task
time limit per test
5 seconds
memory limit per test
512 megabytes
input
standard input
output
standard output
This task is very simple. Given a string S of length n and q queries
each query is on the format i j k which
means sort the substring consisting of the characters from i to j in
non-decreasing order if k = 1 or in non-increasing order if k = 0.
each query is on the format i j k which
means sort the substring consisting of the characters from i to j in
non-decreasing order if k = 1 or in non-increasing order if k = 0.
Output the final string after applying the queries.
Input
The first line will contain two integers n, q (1 ≤ n ≤ 105, 0 ≤ q ≤ 50 000),
the length of the string and the number of queries respectively.
the length of the string and the number of queries respectively.
Next line contains a string S itself. It contains only lowercase English letters.
Next q lines will contain three integers each i, j, k (1 ≤ i ≤ j ≤ n, ).
Output
Output one line, the string S after applying the queries.
Sample test(s)
input
10 5
abacdabcda
7 10 0
5 8 1
1 4 0
3 6 0
7 10 1
10 5
abacdabcda
7 10 0
5 8 1
1 4 0
3 6 0
7 10 1
output
cbcaaaabdd
input
10 1
agjucbvdfk
1 10 1
10 1
agjucbvdfk
1 10 1
output
abcdfgjkuv
Note
First sample test explanation:
题意:就是说给你一个n长度字符串,进行q次操作,k=1时要对着一个区间的字符进行
非递减排序,k=2时相反,最后输出操作后的字符串
思路:正常写法肯定超时,那就可肯定要优化,采取线段树优化查询和更改都是log2(n)
线短路算法:https://blog.csdn.net/zearot/article/details/48299459
代码有解析,我就不多说了!
上代码:
1 #include<stdio.h> 2 #include<iostream> 3 #include<algorithm> 4 #include<string.h> 5 using namespace std; 6 const int maxn=100005; 7 #define lson(x) x<<1 8 #define rson(x) x<<1|1 9 #define L(x) tree[x].l; 10 #define R(x) tree[x].r; 11 #define Hav[x] tree[x].hav 12 #define Lazy(x) tree[x].lazy 13 char ans[maxn],str[maxn]; 14 int cnt[27]; 15 struct Segtree //声明26棵树来存每个字母 16 { 17 int l,r,hav,m,lazy; 18 }tree[27][maxn<<2]; 19 void pushup(int root,int id) //区间求和,因为一个父区间中某个字母的个数是所有子区间字母个数的和 20 { 21 tree[id][root].hav=tree[id][root<<1].hav+tree[id][root<<1|1].hav; 22 } 23 void pushdown(int root,int id) //下推标记,在这过程中将所个区间全部填满,(为什么填满要看主函数) 24 { 25 if(tree[id][root].lazy<0) return; 26 tree[id][root<<1].hav=(tree[id][root<<1].r-tree[id][root<<1].l)*tree[id][root].lazy; 27 tree[id][root<<1].lazy=tree[id][root].lazy; 28 tree[id][root<<1|1].hav=(tree[id][root<<1|1].r-tree[id][root<<1|1].l)*tree[id][root].lazy; 29 tree[id][root<<1|1].lazy=tree[id][root].lazy; 30 tree[id][root].lazy=-1; //父标记删除,子标记续上 31 } 32 //void build(int l,int r,int root,int id) 33 //{ //建一颗总长度为r-l+1的树,其中第一个父节点区间长度最大,处于树的顶端,剩下的依次减少,且相互之间不能重复 34 // int mid=(l+r)>>1; 35 // tree[id][root].m=mid; //记录中间节点,中后可能会使用(我后面代码有的时候没有使用) 36 // tree[id][root].l=l; 37 // tree[id][root].r=r; 38 // tree[id][root].lazy=-1; //标记复原位 39 // if(r-l==1) //按模板来说应该是r==l时才进入,但是主函数中字符数组存值是从0--n-1, 40 // { //但是我建图的时候用的是1--n+1,其实n+1并没有,所以就相当于这个时候已经到树的最底部了 41 // //这一点多做一些题就会学到好些骚操作 42 // tree[id][root].hav=(str[l-1]==‘a‘+id); 43 // return; 44 // } 45 // build(l,mid,root<<1,id); 46 // build(mid+1,r,root<<1|1,id); 47 // pushup(root,id); 48 //} 49 void build(int l,int r,int n,int id) 50 51 { 52 53 int m=(l+r)>>1; 54 55 tree[id][n].l=l; 56 57 tree[id][n].r=r; 58 59 tree[id][n].m=m; 60 61 tree[id][n].lazy=-1; 62 if(r-l==1) 63 { 64 tree[id][n].hav=(str[l-1]==‘a‘+id); 65 return; 66 } 67 68 if(r-l==1) 69 70 { 71 72 tree[id][n].hav=(str[l-1]==‘a‘+id); 73 74 return; 75 76 } 77 78 build(l,m,n<<1,id); 79 //因为当(n+n+1)>>1的值为n,一旦加一,那就超范围了 80 //而且本题再给树底部赋值的时候是用str[l-1]来判断的,由此可见,Segtree结构体里面存的范围和实际操作相差一 81 build(m,r,n<<1|1,id); //————————我的代码是这里错了,我写的是“m+1”—————————————— 82 83 pushup(n,id); 84 85 } 86 //void update(int l,int r,int root,int ops,int id) 87 //{ //更新操作,把给出的这一部分区间全部填满(这里说的全部填满就是tree[id][root].hav=(tree[id][root].r-tree[id][root].l)*1(数字);) 88 // if(tree[id][root].l==l && tree[id][root].r==r) 89 // { 90 // tree[id][root].hav=(tree[id][root].r-tree[id][root].l)*ops; 91 // tree[id][root].lazy=ops; 92 // return; 93 // } 94 // //先下推标记,要不然有的区间还没更新就去查找就会错 95 // pushdown(root,id); 96 // int mid=(tree[id][root].l+tree[id][root].r)>>1; 97 // if(r<=mid) //满足这一个或下一个条件就说明所求区间就在此父节点的一个子区间内 98 // { 99 // update(l,r,root<<1,ops,id); 100 // } 101 // else if(l>=mid) 102 // { 103 // update(l,r,root<<1|1,ops,id); 104 // } 105 // else 106 // { 107 // update(l,mid,root<<1,ops,id); 108 // update(mid,r,root<<1|1,ops,id); 109 // } 110 // pushup(root,id); 111 //} 112 void update(int l,int r,int n,int op,int id) 113 114 { 115 116 if(tree[id][n].l==l && tree[id][n].r==r) 117 118 { 119 120 tree[id][n].hav=(tree[id][n].r-tree[id][n].l)*op; 121 122 tree[id][n].lazy=op; 123 124 return; 125 126 } 127 128 pushdown(n,id); 129 130 if(r<=tree[id][n].m)update(l,r,n<<1,op,id); 131 132 else if(l>=tree[id][n].m)update(l,r,n<<1|1,op,id); 133 134 else 135 136 { 137 138 update(l,tree[id][n].m,n<<1,op,id); 139 140 update(tree[id][n].m,r,n<<1|1,op,id); 141 142 } 143 144 pushup(n,id); 145 146 } 147 int query(int l,int r,int n,int id) 148 149 { 150 151 if(tree[id][n].l==l && tree[id][n].r==r) 152 153 return tree[id][n].hav; 154 155 pushdown(n,id); 156 157 if(r<=tree[id][n].m) 158 159 return query(l,r,n<<1,id); 160 161 if(l>=tree[id][n].m) 162 163 return query(l,r,n<<1|1,id); 164 165 return query(l,tree[id][n].m,n<<1,id) 166 167 +query(tree[id][n].m,r,n<<1|1,id); //————————这里的tree[id][n].m也不能多加一———————————————— 168 169 } 170 //int query(int l,int r,int root,int id) 171 //{ //查找和更新大多都一样 172 // if(tree[id][root].l==l && tree[id][root].r==r) 173 // { 174 // return tree[id][root].hav; 175 // } 176 // pushdown(root,id); 177 // int mid=(tree[id][root].l+tree[id][root].r)>>1; 178 // if(r<=mid) return query(l,r,root<<1,id); 179 // else if(l>=mid) return query(l,r,root<<1|1,id); 180 // return query(l,mid,root<<1,id)+query(mid+1,r,root<<1|1,id); 181 //} 182 //int main() 183 //{ 184 // int n,q; 185 // scanf("%d%d",&n,&q); 186 // scanf("%s",str); 187 // for(int i=0;i<26;++i) build(1,n+1,1,i); 188 // int l,r,k; 189 // while(q--) 190 // { 191 // scanf("%d%d%d",&l,&r,&k); 192 // for(int i=0;i<26;++i) 193 // { 194 // cnt[i]=query(l,r+1,1,i); 195 // update(l,r+1,1,0,i); 196 // } 197 // if(k==1) 198 // { 199 // int loc=l; 200 // for(int i=0;i<26;++i) 201 // { 202 // if(cnt[i]) update(loc,loc+cnt[i],1,1,i); 203 // loc+=cnt[i]; 204 // } 205 // } 206 // else 207 // { 208 // int loc=l; 209 // for(int i=25;i>=0;--i) 210 // { 211 // if(cnt[i]) update(loc,loc+cnt[i],1,1,i); 212 // loc+=cnt[i]; 213 // } 214 // } 215 // } 216 // for(int i=1;i<=n;++i) 217 // { 218 // for(int j=0;j<26;++j) 219 // { 220 // if(query(i,i+1,1,j)) 221 // { 222 // ans[i-1]=‘a‘+j; 223 // break; 224 // } 225 // } 226 // } 227 // printf("%s\n",ans); 228 // return 0; 229 //} 230 int main() 231 232 { 233 234 int n,q; 235 236 scanf("%d%d",&n,&q); 237 238 scanf("%s",str); 239 240 for(int i=0;i<26;i++)build(1,n+1,1,i); 241 //先按照原来的字符在数组里面的顺序对线段树进行填充 242 int l,r,k; 243 244 while(q--) 245 246 { 247 248 scanf("%d%d%d",&l,&r,&k); 249 250 for(int i=0;i<26;i++) 251 252 { 253 254 cnt[i]=query(l,r+1,1,i); 255 //找出来这个被要求的区间内有多少这个字母 256 update(l,r+1,1,0,i); 257 //再把这个区间全部初始化为0 258 } 259 //判断一下是让顺序还是逆序,如果是按顺序排,那就让填充的时候字母正着填充 260 if(k==1) 261 262 { 263 264 int loc=l; 265 266 for(int i=0;i<26;i++) 267 268 { 269 270 if(cnt[i]>0)update(loc,loc+cnt[i],1,1,i); 271 272 loc+=cnt[i]; 273 274 } 275 276 } 277 278 else 279 280 { 281 282 int loc=l; 283 284 for(int i=25;i>=0;i--) 285 286 { 287 288 if(cnt[i]>0)update(loc,loc+cnt[i],1,1,i); 289 290 loc+=cnt[i]; 291 292 } 293 294 } 295 296 } 297 //这个就相当于一个位置一个位置的查询 298 for(int i=1;i<=n;i++) 299 300 for(int j=0;j<26;j++) 301 302 if(query(i,i+1,1,j)) 303 304 { 305 306 ans[i-1]=‘a‘+j; 307 308 break; 309 310 } 311 312 printf("%s\n",ans); 313 314 return 0; 315 316 }
以上是关于Codeforces 558E A Simple Task (计数排序&&线段树优化)的主要内容,如果未能解决你的问题,请参考以下文章
codeforces 558E A Simple Task 线段树