概述
题目
给你一个长度为n(n<=1e5)的字符串,
和q(q<=5e4)次修改操作,
每次修改输入i,j,k三个数,
k==1,表示对[i,j]区间按字典序不降序排序
k==0,表示对[i,j]区间按字典序不增序排序
要求输出修改之后的字符串
思路来源
归神の例会
题解
开26棵线段树,重排时,
只需先类似计数排序统计出[i,j]段每个字母出现的个数,
再根据k的值选择增序或降序,
对每棵线段树内对应的区间位置先清零后在对应位置赋1即可
查询时,每个位置单点查询有没有字母,查到就break
心得
其实还是区间赋值啦,板子改一改就能AC了
只是要注意几个细节,比如输入字符串的下标,num[j]==0时直接跳过等等
只是自己写的最后询问答案时候,变成了26棵树一一logn询问,总感觉有点不妥……
所幸复杂度没超,总复杂度O(26*(n+q)*logn)
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
//基数排序思想 统计出来每个区间对应'a'到'z'各多少个字母
//'a'-'z'每个字母开一棵线段树用于区间赋值
char s[maxn];
int num[26];
int pos;//下一字母的起始位置
int dat[26][maxn*5],cov[26][maxn*5];
int n,q;
int l,r,op;
void pushup(int a[],int b[],int p)
{
a[p]=a[p<<1]+a[p<<1|1];
if((~b[p<<1])&&(~b[p<<1|1]))b[p]=-1;
}
void build(int p,int l,int r)
{
for(int i=0;i<26;++i)
cov[i][p]=-1;//-1表示没有操作,与区间赋0区别
if(l==r)
{
for(int i=0;i<26;++i)
{
if(i==s[l]-'a')dat[i][p]=1;
else dat[i][p]=0;
}
return;
}
int mid=(l+r)/2;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
for(int i=0;i<26;++i)
pushup(dat[i],cov[i],p);
}
void pushdown(int a[],int b[],int p,int l,int r)
{
if(~b[p])
{
int mid=(l+r)/2;
a[p<<1]=b[p]*(mid-l+1);
a[p<<1|1]=b[p]*(r-mid);
b[p<<1]=b[p];
b[p<<1|1]=b[p];
b[p]=-1;
}
}
void update(int a[],int b[],int p,int l,int r,int ql,int qr,int v)//区间赋1或赋0
{
if(ql<=l&&r<=qr)
{
a[p]=v*(r-l+1);
b[p]=v;
return;
}
pushdown(a,b,p,l,r);
int mid=(l+r)/2;
if(ql<=mid)update(a,b,p<<1,l,mid,ql,qr,v);
if(qr>mid)update(a,b,p<<1|1,mid+1,r,ql,qr,v);
pushup(a,b,p);
}
int ask(int a[],int b[],int p,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)return a[p];
pushdown(a,b,p,l,r);
int mid=(l+r)/2,ans=0;
if(ql<=mid)ans+=ask(a,b,p<<1,l,mid,ql,qr);
if(qr>mid)ans+=ask(a,b,p<<1|1,mid+1,r,ql,qr);
return ans;
}
int main()
{
scanf("%d%d",&n,&q);
scanf("%s",s+1);//细节1:[1,n]
build(1,1,n);
for(int i=1;i<=q;++i)
{
scanf("%d%d%d",&l,&r,&op);
pos=l;
//细节2:清零会被赋值覆盖 故不用清零
for(int j=0;j<26;++j)
{
num[j]=ask(dat[j],cov[j],1,1,n,l,r);
update(dat[j],cov[j],1,1,n,l,r,0);
}
if(op==1)
{
for(int j=0;j<26;++j)
{
if(!num[j])continue;//细节3
update(dat[j],cov[j],1,1,n,pos,pos+num[j]-1,1);//pos+num[j]-1>=pos 要求num[j]>=1
pos=pos+num[j];
}
}
else
{
for(int j=25;j>=0;--j)
{
if(!num[j])continue;
update(dat[j],cov[j],1,1,n,pos,pos+num[j]-1,1);
pos=pos+num[j];
}
}
}
for(int i=1;i<=n;++i)
{
for(int j=0;j<26;++j)
{
if(ask(dat[j],cov[j],1,1,n,i,i)>0)
{
putchar(j+'a');
break;
}
}
}
puts("");
return 0;
}
最后
以上就是英俊小松鼠为你收集整理的CodeForces - 558E.A Simple Task(线段树-区间重排)的全部内容,希望文章能够帮你解决CodeForces - 558E.A Simple Task(线段树-区间重排)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复