【做题记录】区间排序—线段树
1. CF558E A Simple Task
題意:
給定由小寫字母組成的字符串 \(s\) 每一次操作如下:
-
\(opt=0\) :將 \([l,r]\) 降序排序
-
\(opt=1\) :將 \([l,r]\) 升序排序
輸出最終字符串
題解:
大致思想為,建 \(26\) 棵線段樹,代表每一種字母。
操作:區間查詢出每一種字母的個數,記為 \(cnt[c]\) 。
排序:按照 從 \(a\) 至 \(z\) 或 從 \(z\) 至 \(a\) 依次向后覆蓋 \(cnt[i]\) 個位置 。
輸出答案:對于每一位,枚舉 \([a,z]\) ,找出覆蓋這一位的字母 。
代碼:
#include<bits/stdc++.h> using namespace std; #define Maxn 100005 #define Maxc 27 int n,m; struct Tree {int sum,ch_laz; }tree[Maxc][Maxn<<2]; int cnt[Maxc]; void build(int opt,int p,int nl,int nr) {tree[opt][p].ch_laz=-1;if(nl==nr) return;int mid=(nl+nr)>>1;if(mid>=nl) build(opt,p<<1,nl,mid);if(mid<nr) build(opt,p<<1|1,mid+1,nr); } void pushdown(int opt,int p,int nl,int nr) {if(tree[opt][p].ch_laz==-1) return;int mid=(nl+nr)>>1;tree[opt][p<<1].sum=(mid-nl+1)*tree[opt][p].ch_laz;tree[opt][p<<1|1].sum=(nr-mid)*tree[opt][p].ch_laz;tree[opt][p<<1].ch_laz=tree[opt][p<<1|1].ch_laz=tree[opt][p].ch_laz;tree[opt][p].ch_laz=-1; } void pushup(int opt,int p) {tree[opt][p].sum=tree[opt][p<<1].sum+tree[opt][p<<1|1].sum; } void change(int opt,int p,int nl,int nr,int l,int r,int x) {if(nl>=l && nr<=r){tree[opt][p].sum=(nr-nl+1)*x;tree[opt][p].ch_laz=x;return;}pushdown(opt,p,nl,nr);int mid=(nl+nr)>>1;if(mid>=l) change(opt,p<<1,nl,mid,l,r,x);if(mid<r) change(opt,p<<1|1,mid+1,nr,l,r,x);pushup(opt,p); } int query(int opt,int p,int nl,int nr,int l,int r) {if(nl>=l && nr<=r) return tree[opt][p].sum;pushdown(opt,p,nl,nr);int mid=(nl+nr)>>1,ret=0;if(mid>=l) ret+=query(opt,p<<1,nl,mid,l,r);if(mid<r) ret+=query(opt,p<<1|1,mid+1,nr,l,r);pushup(opt,p);return ret; } int main() {//freopen(".in","r",stdin);//freopen(".out","w",stdout);cin>>n>>m;for(int i=0;i<26;i++) build(i,1,1,n);char x;for(int i=1;i<=n;i++) cin>>x,change(x-'a',1,1,n,i,i,1);for(int i=1,l,r,k;i<=m;i++){cin>>l>>r>>k;memset(cnt,0,sizeof(cnt));if(k){for(int i=0;i<26;i++) cnt[i]=query(i,1,1,n,l,r),change(i,1,1,n,l,r,0);for(int i=0,pos=l;i<26;i++) if(cnt[i]) change(i,1,1,n,pos,pos+cnt[i]-1,1),pos+=cnt[i];}else{for(int i=25;i>=0;i--) cnt[i]=query(i,1,1,n,l,r),change(i,1,1,n,l,r,0);for(int i=25,pos=l;i>=0;i--) if(cnt[i]) change(i,1,1,n,pos,pos+cnt[i]-1,1),pos+=cnt[i];}}for(int i=1;i<=n;i++) for(int j=0;j<26;j++)if(query(j,1,1,n,i,i)) { printf("%c",j+'a'); break; }printf("\n");//fclose(stdin);//fclose(stdout);return 0; }2. P2824 [HEOI2016/TJOI2016]排序
題意:
給定由 \([1,n]\) 組成的排列,每一次操作如下:
-
\(opt=0\) :將 \([l,r]\) 升序排序
-
\(opt=1\) :將 \([l,r]\) 降序排序
最后輸出第 \(Pos\) 位的值。
題解:
與上一題不同的是:這里有 \(n\) 種元素,但是只用查詢一位的值 。
考慮二分答案!!
二分第 \(Pos\) 位的取值,將大于等于 \(mid\) 的值都改為 \(1\) ,將小于 \(mid\) 的值都改為 \(0\) ,離線進行一遍所有操作。
若操作完后第 \(Pos\) 位為 \(1\) ,則 \(ans\ge mid\) ,否則 \(ans<mid\) 。
注意:(代碼第 \(72\) 行)
cnt=query(1,1,n,l[i],r[i]); 中, \(cnt\) 的可能為 \(0\) ,在接下來的覆蓋中可能會出現 \(l>r\) ,應該及時判斷 。
代碼:
#include<bits/stdc++.h> using namespace std; #define Maxn 100005 typedef long long ll; inline int rd() {int x=0;char ch,t=0;while(!isdigit(ch = getchar())) t|=ch=='-';while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();return x=t?-x:x; } int n,m,Pos,L,R,ans; int a[Maxn],tmp[Maxn],opt[Maxn],l[Maxn],r[Maxn]; struct Data {int sum,laz; }tree[Maxn<<2]; void pushup(int p) {tree[p].sum=tree[p<<1].sum+tree[p<<1|1].sum; } void pushdown(int p,int nl,int nr) {if(tree[p].laz!=-1){int mid=(nl+nr)>>1;tree[p<<1].sum=(mid-nl+1)*tree[p].laz;tree[p<<1|1].sum=(nr-mid)*tree[p].laz;tree[p<<1].laz=tree[p<<1|1].laz=tree[p].laz;tree[p].laz=-1;} } void build(int p,int nl,int nr) {tree[p].sum=0,tree[p].laz=-1;if(nl==nr) { tree[p].sum=tmp[nl]; return; }int mid=(nl+nr)>>1;build(p<<1,nl,mid),build(p<<1|1,mid+1,nr);pushup(p); } void change(int p,int nl,int nr,int l,int r,int k) {if(nl>=l && nr<=r){tree[p].sum=(nr-nl+1)*k;tree[p].laz=k;return;}pushdown(p,nl,nr);int mid=(nl+nr)>>1;if(mid>=l) change(p<<1,nl,mid,l,r,k);if(mid<r) change(p<<1|1,mid+1,nr,l,r,k);pushup(p); } int query(int p,int nl,int nr,int l,int r) {if(nl>=l && nr<=r) return tree[p].sum;pushdown(p,nl,nr);int mid=(nl+nr)>>1,ret=0;if(mid>=l) ret+=query(p<<1,nl,mid,l,r);if(mid<r) ret+=query(p<<1|1,mid+1,nr,l,r);pushup(p);return ret; } bool check(int val) {for(int i=1;i<=n;i++) tmp[i]=(a[i]>=val)?1:0;build(1,1,n);for(int i=1,cnt;i<=m;i++){cnt=query(1,1,n,l[i],r[i]);change(1,1,n,l[i],r[i],0);if(!cnt) continue;if(opt[i]==0) change(1,1,n,r[i]-cnt+1,r[i],1);else change(1,1,n,l[i],l[i]+cnt-1,1);}return query(1,1,n,Pos,Pos); } int main() {//freopen(".in","r",stdin);//freopen(".out","w",stdout);n=rd(),m=rd();for(int i=1;i<=n;i++) a[i]=rd();for(int i=1;i<=m;i++) opt[i]=rd(),l[i]=rd(),r[i]=rd();Pos=rd();L=1,R=n,ans=1;while(L<=R){int mid=(L+R)>>1;if(check(mid)) ans=mid,L=mid+1;else R=mid-1;}printf("%d\n",ans);//fclose(stdin);//fclose(stdout);return 0; }總結
以上是生活随笔為你收集整理的【做题记录】区间排序—线段树的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 自己配的电脑已经用八九年了,依然很流畅
- 下一篇: 数学基础知识(高精、快速幂、龟速乘……)