string [线段树优化桶排]
题意大概是给你一个字符串,1e5次修改,每次给一个区间升序排列或降序排列,最后输出这个字符串;
其实是个挺裸的线段树优化题;但是我没有意识去结合桶排,扑该.....
首先 1.40分算法
O(NMlogN)
1 inline void update(int k){ 2 for(int i=1;i<=26;++i){ 3 tmp[k].tong[i]=tmp[ls].tong[i]+tmp[rs].tong[i]; 4 } 5 } 6 7 void ud(int k,int l,int r,int L,int R){ 8 if(L<=l&&r<=R)return void(); 9 if(L<=mid)ud(ls,l,mid,L,R); 10 if(R>mid)ud(rs,mid+1,r,L,R); 11 update(k); 12
直接sort
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define val(x) (x-'a'+1) 4 int s[100050],N,M; 5 char S[100050]; 6 bool cmp1(int a,int b){return a<b;} 7 bool cmp0(int a,int b){return a>b;} 8 int main() 9 { 10 //freopen("da.in","r",stdin); 11 // freopen("ac.out","w",stdout); 12 scanf("%d%d",&N,&M); 13 scanf("%s",S+1); 14 for(int i=1;i<=N;++i)s[i]=val(S[i]); 15 for(int i=1,l,r,opt;i<=M;++i){ 16 scanf("%d%d%d",&l,&r,&opt); 17 if(!opt)sort(s+l,s+r+1,cmp0); 18 else sort(s+l,s+r+1,cmp1); 19 // if(clock()>999900)break; 20 } 21 char c; 22 for(int i=1;i<=N;++i){ 23 c=s[i]+'a'-1; 24 printf("%c",c); 25 } 26 printf("\n"); 27 }
其次 2.仍是40分算法:
其实也是出题人没良心,我们可以直接桶排,复杂度O(NM);起码降下了一个log,但还是40分;
代码就不提供了;
最后 3.100分算法:
思路:结合2.中的桶排思想,我们发现修改排序一次可以只有O(N)的复杂度,而N,M<=1e5,自然想到线段树优化桶排;
而这样想的裸想法的复杂度是O(MNlogN)氧化钙!跟暴力一样!
但是可以发现 你的数值N的值域是你的字母,那N就只有26啦;
最后O(MlogN*26)
分析一下 M是询问; logN是线段树上的查询(合并桶而需查询你所修改的区域的桶来合成一个新的桶); 26是每次桶合并所需复杂度;
好,接下来,我们每次修改一个区间进行排序,就是把对应区间的桶合并成一个新桶,然后用这个新桶进行区间修改,但是注意,这个区间修改一定不要去真的O(N)区间赋值
那就成为了真正的O(NMlogN)了;
那怎么办?
我们可以打懒标记啊!别说你像我一样不会打标记......
那我们就可以标记哪个区间被修改了然后访问到再down一下,最后一遍dfs来一次全down统计答案对吧;
注意,这里down很长很长
1 void down(int k){ 2 if(!tmp[k].lazy)return ; 3 register int sz=len(ls),pl=0,pr=0; 4 memset(tmp[ls].tong,0,sizeof(tmp[ls].tong)); 5 memset(tmp[rs].tong,0,sizeof(tmp[rs].tong)); 6 if(tmp[k].opt){ 7 for(int i=1;i<=26;++i){ 8 if(tmp[k].tong[i]<=sz) 9 { 10 tmp[ls].tong[i]=tmp[k].tong[i],sz-=tmp[k].tong[i]; 11 } 12 else { 13 tmp[ls].tong[i]=sz;sz=i;break; 14 } 15 } 16 tmp[rs].tong[sz]=tmp[k].tong[sz]-tmp[ls].tong[sz]; 17 if(len(rs)==1&&tmp[rs].tong[sz]==1){pr=sz;} 18 register int zs=len(rs)-tmp[rs].tong[sz]; 19 for(int i=sz+1;i<=26;++i){ 20 tmp[rs].tong[i]=tmp[k].tong[i]; 21 zs-=tmp[k].tong[i]; 22 if(!zs)break; 23 } 24 } 25 else { 26 for(int i=26;i>=1;--i){ 27 if(tmp[k].tong[i]<=sz) 28 { 29 tmp[ls].tong[i]=tmp[k].tong[i],sz-=tmp[k].tong[i]; 30 } 31 else { 32 tmp[ls].tong[i]=sz;sz=i;break; 33 } 34 } 35 tmp[rs].tong[sz]=tmp[k].tong[sz]-tmp[ls].tong[sz]; 36 register int zs=len(rs)-tmp[rs].tong[sz]; 37 for(int i=sz-1;i>=1;--i){ 38 tmp[rs].tong[i]=tmp[k].tong[i]; 39 zs-=tmp[k].tong[i]; 40 if(!zs)break; 41 } 42 } 43 if(tmp[ls].l!=tmp[ls].r)tmp[ls].lazy=1; 44 else tmp[ls].lz=pl;tmp[ls].opt=tmp[k].opt; 45 if(tmp[rs].l!=tmp[rs].r)tmp[rs].lazy=1; 46 else tmp[rs].lz=pr;tmp[rs].opt=tmp[k].opt; 47 tmp[k].lazy=0;tmp[k].opt=0; 48 }
当然我不像Deepin某一样缩行;
其实这里的down就是把你线段树上这个节点的两个儿子所对应区间的桶重新赋值的过程,当然赋值一次需要O(52);
Tip:
还有一点特别容易错的就是
记住每次把你对应修改的区间的桶合并后要立即修改对应区间的桶(区间修改):(这里lg这个vector存放的是你修改的区间的在线段树上的节点编号)
1 void exch(int opt){ 2 sort(lg.begin(),lg.end(),cmp); 3 for(int i=0,sz,tt=1,ts=26;i<lg.size();++i){ 4 sz=len(lg[i]); 5 memset(tmp[lg[i]].tong,0,sizeof(tmp[lg[i]].tong)); 6 if(opt){ 7 for(;tt<=26;++tt){ 8 if(sz>=ans[tt]){ 9 sz-=ans[tt]; 10 tmp[lg[i]].tong[tt]=ans[tt]; 11 } 12 else { 13 tmp[lg[i]].tong[tt]=sz; 14 ans[tt]-=sz;break; 15 } 16 } 17 } 18 else { 19 for(;ts>=1;--ts){ 20 if(sz>=ans[ts]){ 21 sz-=ans[ts]; 22 tmp[lg[i]].tong[ts]=ans[ts]; 23 } 24 else { 25 tmp[lg[i]].tong[ts]=sz; 26 ans[ts]-=sz;break; 27 } 28 } 29 } 30 } 31 }
然后修改完后要立即把你这些修改的区间到根update一遍,毕竟你的懒标记只能维护儿子而不能维护父亲;
1 inline void update(int k){ 2 for(int i=1;i<=26;++i){ 3 tmp[k].tong[i]=tmp[ls].tong[i]+tmp[rs].tong[i]; 4 } 5 } 6 7 8 void ud(int k,int l,int r,int L,int R){ 9 if(L<=l&&r<=R)return void(); 10 if(L<=mid)ud(ls,l,mid,L,R); 11 if(R>mid)ud(rs,mid+1,r,L,R); 12 update(k); 13 }
然后附上代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define val(x) (x-'a'+1) 4 #define mid (l+r>>1) 5 #define ls (k<<1) 6 #define rs (k<<1|1) 7 #define len(x) (tmp[x].r-tmp[x].l+1) 8 int s[100050],N,M; 9 char S[100050]; 10 struct node{ 11 int l;int r; 12 int tong[30]; 13 int lazy,lz,opt; 14 }tmp[100050<<3]; 15 int ans[30]; 16 vector<int>lg; 17 18 inline void update(int k){ 19 for(int i=1;i<=26;++i){ 20 tmp[k].tong[i]=tmp[ls].tong[i]+tmp[rs].tong[i]; 21 } 22 } 23 24 void down(int k){ 25 if(!tmp[k].lazy)return ; 26 register int sz=len(ls),pl=0,pr=0; 27 memset(tmp[ls].tong,0,sizeof(tmp[ls].tong)); 28 memset(tmp[rs].tong,0,sizeof(tmp[rs].tong)); 29 if(tmp[k].opt){ 30 for(int i=1;i<=26;++i){ 31 if(tmp[k].tong[i]<=sz) 32 { 33 tmp[ls].tong[i]=tmp[k].tong[i],sz-=tmp[k].tong[i]; 34 // if(len(ls)==1&&tmp[ls].tong[i]){pl=i;break;} 35 } 36 else { 37 tmp[ls].tong[i]=sz;sz=i;break; 38 } 39 } 40 //for(int i=1;i<=sz-1;++i)tmp[rs].tong[i]=0; 41 //for(int i=sz+1;i<=26;++i)tmp[ls].tong[i]=0; 42 tmp[rs].tong[sz]=tmp[k].tong[sz]-tmp[ls].tong[sz]; 43 if(len(rs)==1&&tmp[rs].tong[sz]==1){pr=sz;} 44 register int zs=len(rs)-tmp[rs].tong[sz]; 45 for(int i=sz+1;i<=26;++i){ 46 tmp[rs].tong[i]=tmp[k].tong[i]; 47 zs-=tmp[k].tong[i]; 48 if(!zs)break; 49 // if(len(rs)==1&&tmp[rs].tong[i]&&!pr)pr=i; 50 } 51 } 52 else { 53 for(int i=26;i>=1;--i){ 54 if(tmp[k].tong[i]<=sz) 55 { 56 tmp[ls].tong[i]=tmp[k].tong[i],sz-=tmp[k].tong[i]; 57 // if(len(ls)==1&&tmp[ls].tong[i]){pl=i;break;} 58 } 59 else { 60 tmp[ls].tong[i]=sz;sz=i;break; 61 } 62 } 63 //for(int i=26;i>=sz+1;--i)tmp[rs].tong[i]=0; 64 //for(int i=1;i<=sz-1;++i)tmp[ls].tong[i]=0; 65 tmp[rs].tong[sz]=tmp[k].tong[sz]-tmp[ls].tong[sz]; 66 //if(len(rs)==1&&tmp[rs].tong[sz]==1){pr=sz;} 67 register int zs=len(rs)-tmp[rs].tong[sz]; 68 for(int i=sz-1;i>=1;--i){ 69 tmp[rs].tong[i]=tmp[k].tong[i]; 70 zs-=tmp[k].tong[i]; 71 if(!zs)break; 72 // if(len(rs)==1&&tmp[rs].tong[i]&&!pr)pr=i; 73 } 74 } 75 if(tmp[ls].l!=tmp[ls].r)tmp[ls].lazy=1; 76 else tmp[ls].lz=pl;tmp[ls].opt=tmp[k].opt; 77 if(tmp[rs].l!=tmp[rs].r)tmp[rs].lazy=1; 78 else tmp[rs].lz=pr;tmp[rs].opt=tmp[k].opt; 79 tmp[k].lazy=0;tmp[k].opt=0; 80 } 81 82 inline void merge(int u,int opt){ 83 down(u); 84 for(register int i=1;i<=26;++i){ 85 ans[i]+=tmp[u].tong[i]; 86 } 87 if(len(u)>1)tmp[u].lazy=1;lg.push_back(u); 88 tmp[u].opt=opt; 89 } 90 91 inline int zip(int k){ 92 //cout<<"laidao!"<<endl; 93 for(int i=1;i<=26;++i)if(tmp[k].tong[i])return i; 94 } 95 96 void find(int k,int l,int r){ 97 if(l==r)return s[l]=zip(k),void(); 98 down(k); 99 find(ls,l,mid); 100 find(rs,mid+1,r); 101 } 102 103 void query(int k,int l,int r,int L,int R,int opt){ 104 if(L<=l&&r<=R)return merge(k,opt),void(); 105 down(k); 106 if(L<=mid)query(ls,l,mid,L,R,opt); 107 if(R>mid)query(rs,mid+1,r,L,R,opt); 108 } 109 110 void ud(int k,int l,int r,int L,int R){ 111 if(L<=l&&r<=R)return void(); 112 if(L<=mid)ud(ls,l,mid,L,R); 113 if(R>mid)ud(rs,mid+1,r,L,R); 114 update(k); 115 } 116 117 void building(int k,int l,int r){ 118 tmp[k].l=l;tmp[k].r=r; 119 if(l==r)return tmp[k].l=tmp[k].r=l,tmp[k].tong[s[l]]=1,tmp[k].lazy=tmp[k].opt=0,void(); 120 building(ls,l,mid);building(rs,mid+1,r); 121 update(k); 122 } 123 124 bool cmp(int u,int v){return tmp[u].l<tmp[v].l;} 125 126 void exch(int opt){ 127 sort(lg.begin(),lg.end(),cmp); 128 for(int i=0,sz,tt=1,ts=26;i<lg.size();++i){ 129 sz=len(lg[i]); 130 memset(tmp[lg[i]].tong,0,sizeof(tmp[lg[i]].tong)); 131 if(opt){ 132 for(;tt<=26;++tt){ 133 if(sz>=ans[tt]){ 134 sz-=ans[tt]; 135 tmp[lg[i]].tong[tt]=ans[tt]; 136 } 137 else { 138 tmp[lg[i]].tong[tt]=sz; 139 ans[tt]-=sz;break; 140 } 141 } 142 } 143 else { 144 for(;ts>=1;--ts){ 145 if(sz>=ans[ts]){ 146 sz-=ans[ts]; 147 tmp[lg[i]].tong[ts]=ans[ts]; 148 } 149 else { 150 tmp[lg[i]].tong[ts]=sz; 151 ans[ts]-=sz;break; 152 } 153 } 154 } 155 } 156 } 157 int main() 158 { 159 //freopen("da.in","r",stdin); 160 //freopen("te.out","w",stdout); 161 scanf("%d%d",&N,&M); 162 scanf("%s",S+1); 163 for(int i=1;i<=N;++i)s[i]=val(S[i]); 164 building(1,1,N); 165 for(int i=1,l,r,opt;i<=M;++i){ 166 scanf("%d%d%d",&l,&r,&opt); 167 memset(ans,0,sizeof(ans));lg.clear(); 168 query(1,1,N,l,r,opt); 169 exch(opt); 170 ud(1,1,N,l,r); 171 } 172 find(1,1,N); 173 char c; 174 for(int i=1;i<=N;++i){ 175 c=s[i]+'a'-1; 176 printf("%c",c); 177 } 178 printf("\n"); 179 //int ppx='P'-'a'+1; 180 //cout<<"ppx="<<ppx<<endl; 181 }