[CSP-S模拟测试]:string(线段树)

题目描述

给定一个由小写字母组成的字符串$s$。

有$m$次操作,每次操作给定$3$个参数$l,r,x$。

如果$x=1$,将$s[l]~s[r]$升序排序;

如果$x=0$,将$s[l]~s[r]$降序排序。

你需要求出最终序列。


输入格式

第一行两个整数$n,m$。
第二行一个字符串$s$。
接下来m行每行三个整数$l,r,x$。


输出格式

一行一个字符串表示答案。


样例

样例输入

5 2
cabcd
1 3 1
3 5 0

样例输出

abdcc


数据范围与提示

对于$40\%$的数据,$n,m\leqslant 1,000$。
对于$100\%$的数据,$n,m\leqslant 100,000$。


题解

看到这道题,我就想到了:[BZOJ4552]:[Tjoi2016&Heoi2016]排序(桶排序)

然而,那道题我使用桶排序卡过的,时限还是$6,000ms$,所以我当场懵逼,线段树是肯定看出来了,但是不知道该怎么操作……

打题一定要打正解挖~

$40\%$算法:

直接用$sort$搞就好了,重载一下运算符,我觉得我说的每一句都是废话……

桶排一分也不能多拿(万恶的出题人)。

时间复杂度:

  $\Theta(m\times n)$(桶排序)。

  $\Theta( m\times n\log n)$(快排)。

期望得分:$40$分。

$100\%$算法:

因为这道题串中只有26个字母,所以就好说多了,用线段树维护区间内$a~z$的个数,每次修改拆成26个修改就行了。

时间复杂度:$\Theta(26\times m\times \log n)$。

期望得分:$100$分。


代码时刻

$40\%$算法:

#include<bits/stdc++.h>
using namespace std;
int n,m,l,r;
bool x;
int a[100001];
char ch[100001];
int t[50];
int st,ed;
void change1()
{
	register int maxn=0,minn=20020923,lft=l;
	for(int i=l;i<=r;i++)
	{
		t[a[i]]++;
		maxn=max(maxn,a[i]);
		minn=min(minn,a[i]);
	}
	for(register int i=minn;i<=maxn;i++)
		while(t[i])
		{
			a[lft++]=i;
			t[i]--;
		}
}
void change2()
{
	int maxn=0,minn=20020923,lft=l;
	for(register int i=l;i<=r;i++)
	{
		t[a[i]]++;
		maxn=max(maxn,a[i]);
		minn=min(minn,a[i]);
	}
	for(register int i=maxn;i>=minn;i--)
		while(t[i])
		{
			a[lft++]=i;
			t[i]--;
		}
}

int main()
{
	st=clock();
	scanf("%d%d%s",&n,&m,ch+1);
	for(register int i=1;i<=n;i++)
		a[i]=ch[i]-'a'+1;
	while(m--)
	{
		scanf("%d%d%d",&l,&r,&x);
		if(x)change1();
		else change2();
	}
	for(register int i=1;i<=n;i++)
		printf("%c",(char)a[i]+'a'-1);
	return 0;
}

$100\%$算法:

#include<bits/stdc++.h>
#define L(x) x<<1
#define R(x) x<<1|1
using namespace std;
int n,m;
char ch[100001];
int tr[400001];
int flag[30];
void pushup(int x){if(tr[L(x)]==tr[R(x)])tr[x]=tr[L(x)];}
void pushdown1(int x){if(tr[x])tr[L(x)]=tr[R(x)]=tr[x];}
void pushdown2(int x){if(tr[x])tr[L(x)]=tr[R(x)]=tr[x];tr[x]=0;}
void build(int x,int l,int r)
{
	if(l==r){tr[x]=ch[l]-'a'+1;return;}
	int mid=(l+r)>>1;
	build(L(x),l,mid);
	build(R(x),mid+1,r);
	pushup(x);
}
void ask(int x,int l,int r,int L,int R)
{
	if(r<L||R<l)return;
	if(L<=l&&r<=R&&tr[x]){flag[tr[x]]+=r-l+1;return;}
	int mid=(l+r)>>1;
	pushdown1(x);
	ask(L(x),l,mid,L,R);
	ask(R(x),mid+1,r,L,R);
}
void change(int x,int l,int r,int L,int R,int v)
{
	if(r<L||R<l)return;
	if((L<=l&&r<=R)||tr[x]==v){tr[x]=v;return;}
	int mid=(l+r)>>1;
	pushdown2(x);
	change(L(x),l,mid,L,R,v);
	change(R(x),mid+1,r,L,R,v);
	pushup(x);
}
void print(int x,int l,int r)
{
	if(tr[x]){for(int i=l;i<=r;i++)printf("%c",(char)tr[x]+'a'-1);return;}
	int mid=(l+r)>>1;
	print(L(x),l,mid);
	print(R(x),mid+1,r);
}
int main()
{
	scanf("%d%d%s",&n,&m,ch+1);
	build(1,1,n);
	while(m--)
	{
		int l,r,x;
		scanf("%d%d%d",&l,&r,&x);
		memset(flag,0,sizeof(flag));
		ask(1,1,n,l,r);
		if(x)for(int i=1;i<=26;i++){change(1,1,n,l,l+flag[i]-1,i);l+=flag[i];}
		else for(int i=26;i>=1;i--){change(1,1,n,l,l+flag[i]-1,i);l+=flag[i];}
	}
	print(1,1,n);
	return 0;
}

rp++

posted @ 2019-08-01 21:43  HEOI-动动  阅读(267)  评论(0编辑  收藏  举报