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 }

update

然后附上代码

  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 }

ac

原文地址:https://www.cnblogs.com/wang-hesong/p/11286548.html

时间: 2024-10-10 14:36:28

string [线段树优化桶排]的相关文章

Codeforces 558E A Simple Task (计数排序&amp;&amp;线段树优化)

题目链接:http://codeforces.com/contest/558/problem/E E. A Simple Task time limit per test5 seconds memory limit per test512 megabytes inputstandard input outputstandard output This task is very simple. Given a string S of length n and q queries each quer

hdu 4719 Oh My Holy FFF(dp线段树优化)

Oh My Holy FFF Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 848    Accepted Submission(s): 219 Problem Description N soldiers from the famous "*FFF* army" is standing in a line, from le

Weak Pair---hud5877大连网选(线段树优化+dfs)

题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5877  题意:给你一颗树,有n个节点,每个节点都有一个权值v[i]:现在求有多少对(u,v)满足u是v的祖先,并且au*av<=k, k是已知的: 思路:从根节点开始dfs遍历整棵树,当遍历到某点u时,已经在栈中的节点都是u的祖先的,所以我们只要找到在栈中的节点有多少个是<=k/a[u]的即可: 由于n的值最大可达到10e5,所以直接查找是会TLE的,我们可以用线段树优化即可:在dfs

HDU 1025-Constructing Roads In JGShining&#39;s Kingdom(最长不降子序列,线段树优化)

分析: 最长不降子序列,n很大o(n^2)肯定超,想到了小明序列那个题用线段树维护前面的最大值即可 该题也可用二分搜索来做. 注意问题输出时的坑,路复数后加s #include <map> #include <set> #include <list> #include <cmath> #include <queue> #include <stack> #include <cstdio> #include <vecto

D. Babaei and Birthday Cake---cf629D(最长上升子序列和+线段树优化)

http://codeforces.com/problemset/problem/629/D 题目大意: 我第一反应就是求最长上升子序列和  但是数值太大了  不能直接dp求  可以用线段树优化一下 #include<stdio.h> #include<string.h> #include<stdio.h> #include<math.h> #include<iostream> #include<algorithm> using na

[Poi2010]Monotonicity 2 (线段树优化DP)

题目描述 给出N个正整数a[1..N],再给出K个关系符号(>.<或=)s[1..k].选出一个长度为L的子序列(不要求连续),要求这个子序列的第i项和第i+1项的的大小关系为s[(i-1)mod K+1].求出L的最大值. 输入 第一行两个正整数,分别表示N和K (N, K <= 500,000).第二行给出N个正整数,第i个正整数表示a[i] (a[i] <= 10^6).第三行给出K个空格隔开关系符号(>.<或=),第i个表示s[i]. 输出 一个正整数,表示L的

HDU4719-Oh My Holy FFF(DP线段树优化)

Oh My Holy FFF Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 606    Accepted Submission(s): 141 Problem Description N soldiers from the famous "*FFF* army" is standing in a line, from le

题解 HDU 3698 Let the light guide us Dp + 线段树优化

http://acm.hdu.edu.cn/showproblem.php?pid=3698 Let the light guide us Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 62768/32768 K (Java/Others) Total Submission(s): 759    Accepted Submission(s): 253 Problem Description Plain of despair was

BZOJ 1835 基站选址(线段树优化DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1835 题意:有N个村庄坐落在一条直线上,第 i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村 庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了.如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi.现在的问题是,选择基站的位 置,使得总费用最小. 思路: 另外,程序中的n=n+1,m=