题解 - CF558E A Simple Task

本文介绍了CF558E A Simple Task的解题过程,作者经历了3小时解决该问题。题目要求对长度不超过105的小写字母序列进行升序或倒序操作,并给出m次操作后的序列。解决方案是利用26棵线段树,分别维护每个字母的状态,通过线段树的基本操作实现序列的更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题解 - CF558E A Simple Task\mathrm{CF558E \ A \ Simple \ Task}CF558E A Simple Task

吐槽一下:

  • 这道题目足足做了我333个小时,我stmstmstm真的要吐血了。

题目意思

就是给你一长为n(≤105)n(\leq 10^5)n(105)个小写字母序列,支持两种操作:

  • (l,r,0)(l,r,0)(l,r,0):将[l,r][l,r][l,r]升序
  • (l,r,1)(l,r,1)(l,r,1):将[l,r][l,r][l,r]倒序

求出m(≤105)m(\leq 10^5)m(105)次操作后的序列。

Sol\mathrm{Sol}Sol

  • 可以说是一道经典套路题吧
  • 我们首先建262626棵线段树维护每一个字母,相当于对于升序我们只要对于这段区间里的字母按a−z\mathrm{a-z}az的顺序依次加入,不停更新加入左端点即可,右端点即为左端点加这个字母的数量。倒序也同理,只要z−a\mathrm{z-a}za的顺序即可。
  • 然后就是线段树的基本操作了(嘻嘻嘻,我调了半年。
  • 说句实话:对于大多数题目,套路就是硬道理。。。

Code\mathrm{Code}Code

#include <bits/stdc++.h>
using namespace std;
 
inline int read()
{
	int sum=0,ff=1; char ch=getchar();
	while(!isdigit(ch))
	{
		if(ch=='-') ff=-1;
		ch=getchar();
	}
	while(isdigit(ch))
		sum=sum*10+(ch^48),ch=getchar();
	return sum*ff;
}
 
const int N=1e5+5;
 
int n,m,ans;
char ch[100001],res[100001];
 
struct seg
{
	int tr[31][400001],laz[31][400001];
	inline void init()
	{
		for ( int i=1;i<=26;i++ )  
			memset(laz[i],-1,sizeof(laz[i]));
	}
	inline void build(int rt,int l,int r)
	{
		if(l==r)
		{
			tr[ch[l]-'a'+1][rt]=1;
			return;
		}
		int mid=(l+r)/2;
		build(rt<<1,l,mid);
		build(rt<<1|1,mid+1,r);
		for ( int i=1;i<=26;i++ ) 
			tr[i][rt]=(tr[i][rt<<1]+tr[i][rt<<1|1]);
	}
	inline void push_down(int rt,int l,int r,int col)
	{
		if(laz[col][rt]!=-1)
		{
			tr[col][rt]=laz[col][rt]*(r-l+1);
			int mid=(l+r)/2;
			tr[col][rt<<1]=laz[col][rt]*(mid-l+1);
			tr[col][rt<<1|1]=laz[col][rt]*(r-mid);
			laz[col][rt<<1]=laz[col][rt];
			laz[col][rt<<1|1]=laz[col][rt];
			laz[col][rt]=-1;
		}
	}
	inline void update(int rt,int l,int r,int ll,int rr,int col,int v)
	{
		if(ll<=l&&r<=rr)
		{
			tr[col][rt]=v*(r-l+1);
			laz[col][rt]=v;
			return;
		}
		push_down(rt,l,r,col);
		int mid=(l+r)/2;
		if(ll<=mid) update(rt<<1,l,mid,ll,rr,col,v);
		if(rr>mid) update(rt<<1|1,mid+1,r,ll,rr,col,v);
		tr[col][rt]=tr[col][rt<<1]+tr[col][rt<<1|1];
	}
	inline int query(int rt,int l,int r,int ll,int rr,int col)
	{
		if(ll<=l&&r<=rr) return tr[col][rt];
		push_down(rt,l,r,col);
		int mid=(l+r)/2;
		int sum=0;
		if(ll<=mid) sum+=query(rt<<1,l,mid,ll,rr,col);
		if(rr>mid) sum+=query(rt<<1|1,mid+1,r,ll,rr,col);
		return sum;
	}
	inline void print(int rt,int l,int r)
	{
		if(l==r)
		{
			for ( int i=1;i<=26;i++ ) 
				if(tr[i][rt]) 
				{
					res[l]='a'+i-1;
					break;
				}
			return;
		}
		for ( int i=1;i<=26;i++ ) push_down(rt,l,r,i);
		int mid=(l+r)/2;
		print(rt<<1,l,mid);
		print(rt<<1|1,mid+1,r);
	}				
};
seg T;
 
int main()
{
	n=read();
	m=read();
	scanf("%s",ch+1);
	T.init();
	T.build(1,1,n);
	for (;m--;)
	{
		int op,x,y;
		x=read(),y=read(),op=read();
		if(op==1)
		{
			int tmp=x-1;
            for ( int j=1;j<=26;j++ )
			{
                int cas=T.query(1,1,n,x,y,j);
                if(!cas)continue;
                T.update(1,1,n,x,y,j,0);
                T.update(1,1,n,tmp+1,tmp+cas,j,1);
				tmp=tmp+cas;
            }
		}
		else
		{
			int lp=x-1;
			for ( int j=26;j>=1;j-- )
			{
				int sum=T.query(1,1,n,x,y,j);
				if(!sum) continue;
				T.update(1,1,n,x,y,j,0);
				T.update(1,1,n,lp+1,lp+sum,j,1);
				lp+=sum;

			}
		}
	}
	T.print(1,1,n);
	for ( int i=1;i<=n;i++ ) putchar(res[i]);
	return 0;
}
/*
10 5
abacdabcda
7 10 0
5 8 1
1 4 0
3 6 0
7 10 1
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值