【做题记录】区间排序—线段树
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;
}