T1 string
【题目描述】
给定一个由小写字母组成的字符串s。有m次操作,每次操作给定3个参数l,r,x。如果x=1,将s[l]~s[r]升序排序;如果x=0,将s[l] ~ s[r]降序排序。你需要求出最终序列。
【输入数据】
第一行两个整数n,m。第二行一个字符串s。接下来m行每行三个整数x,l,r。
【输出数据】
一行一个字符串表示答案。
【样例输入】
5 2
cabcd
1 3 1
3 5 0
【样例输出】
abdcc
【数据范围】
对于40%的数据,n,m<=1000。
对于100%的数据,n,m<=100000。
解析
看样子无从下手,但实际上观察字符的范围,只有 a a a~ z z z,所以我们可以对每个字符分别处理。
开一个线段树,里面存 l l l~ r r r分别的 a a a ~ z z z有多少个。这样在每次修改操作之前,先把 l l l ~ r r r的序列变得有序,然后打标记。每次修改到这个区间时标记下传就可以了。
结合代码食用效果更佳。
复杂度 O ( n log n ) O(n \log n) O(nlogn),但常数有点大,估计有100吧。
总结
如果有对区间进行操作的题首选线段树,再根据题目的特殊限定决定维护的东西。
(PS.其实我是想到了线段树的,奈何我太菜了,没有想到分字母修改
T
A
T
TAT
TAT)。
代码
#include<cstdio>
#include<cstring>
#define maxn 100005
#define lid (k<<1)
#define rid (k<<1|1)
using namespace std;
struct node
{
int l,r,fl;
int cnt[30];
}tr[maxn*4];
int n,m;
int val[maxn],sum[30];
char s[maxn];
void update(int k)
{
for(int i=1;i<=26;++i)
tr[k].cnt[i]=tr[lid].cnt[i]+tr[rid].cnt[i];
}
void pushdown(int k)
{
if(!tr[k].fl)return;
for(int i=1;i<=26;++i)
tr[lid].cnt[i]=tr[rid].cnt[i]=0;
int l=tr[k].l,r=tr[k].r;
int mid=(l+r)>>1;
int lenl=mid-l+1,lenr=r-mid;
if(tr[k].fl==1)
{
for(int i=1;i<=26&&lenl;++i)
{
if(lenl<tr[k].cnt[i])
{
tr[lid].cnt[i]+=lenl;
lenl=0;
break;
}
else
{
tr[lid].cnt[i]+=tr[k].cnt[i];
lenl-=tr[k].cnt[i];
}
}
for(int i=26;i>=1&&lenr;--i)
{
if(lenr<tr[k].cnt[i])
{
tr[rid].cnt[i]+=lenr;
lenr=0;
break;
}
else
{
tr[rid].cnt[i]+=tr[k].cnt[i];
lenr-=tr[k].cnt[i];
}
}
tr[lid].fl=tr[rid].fl=1;
}//升序
else if(tr[k].fl==2)
{
for(int i=26;i>=1&&lenl;--i)
{
if(lenl<tr[k].cnt[i])
{
tr[lid].cnt[i]+=lenl;
lenl=0;
break;
}
else
{
tr[lid].cnt[i]+=tr[k].cnt[i];
lenl-=tr[k].cnt[i];
}
}
for(int i=1;i<=26&&lenr;++i)
{
if(lenr<tr[k].cnt[i])
{
tr[rid].cnt[i]+=lenr;
lenr=0;
break;
}
else
{
tr[rid].cnt[i]+=tr[k].cnt[i];
lenr-=tr[k].cnt[i];
}
}
tr[lid].fl=tr[rid].fl=2;
}//降序
tr[k].fl=0;
}
void build(int k,int l,int r)
{
tr[k].l=l;
tr[k].r=r;
tr[k].fl=0;
if(l==r)
{
int v=val[l];
tr[k].cnt[v]=1;
return;
}
int mid=(l+r)>>1;
build(lid,l,mid);
build(rid,mid+1,r);
update(k);
}
void modify(int k,int l,int r,int del)
{
if(tr[k].l>=l&&tr[k].r<=r)
{
tr[k].fl=del;
for(int i=1;i<=26;++i)
tr[k].cnt[i]=0;
int tot=tr[k].r-tr[k].l+1;
if(del==1)
{
for(int i=1;i<=26&&tot;++i)
{
if(tot<sum[i])
{
tr[k].cnt[i]+=tot;
sum[i]-=tot;
tot=0;
break;
}
else
{
tr[k].cnt[i]+=sum[i];
tot-=sum[i];
sum[i]=0;
}
}
}//升序
else
{
for(int i=26;i>=1&&tot;--i)
{
if(tot<sum[i])
{
tr[k].cnt[i]+=tot;
sum[i]-=tot;
tot=0;
break;
}
else
{
tr[k].cnt[i]+=sum[i];
tot-=sum[i];
sum[i]=0;
}
}
}//降序
return;
}//重新排序
int mid=(tr[k].l+tr[k].r)>>1;
pushdown(k);
if(l<=mid)
modify(lid,l,r,del);
if(r>mid)
modify(rid,l,r,del);
update(k);
}
void query(int k,int l,int r)
{
if(tr[k].l>=l&&tr[k].r<=r)
{
for(int i=1;i<=26;++i)
sum[i]+=tr[k].cnt[i];
return;
}
int mid=(tr[k].l+tr[k].r)>>1;
pushdown(k);
if(l<=mid)
query(lid,l,r);
if(r>mid)
query(rid,l,r);
}
void print(int k)
{
if(tr[k].l==tr[k].r)
{
for(int i=1;i<=26;++i)
{
if(tr[k].cnt[i])
printf("%c",i-1+'a');
}
return;
}
pushdown(k);
print(lid);
print(rid);
}
int main()
{
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
scanf("%d%d",&n,&m);
scanf("%s",s+1);
for(int i=1;i<=n;++i)
val[i]=s[i]-'a'+1;
build(1,1,n);
for(int i=1;i<=m;++i)
{
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
if(!x)x=2;
memset(sum,0,sizeof(sum));
query(1,l,r);//求区间内的元素个数
modify(1,l,r,x);//区间修改
}
print(1);
return 0;
}
/*
5 2
cabcd
1 3 1
3 5 0
*/