【树形结构】测试题D - A Simple Task

题意

一个长度为n的字符串,有q条查询。

两种操作:

1. i,j,1 把字符串从i到j的部分升序排列

2. i,j,0把字符串从i到j的部分降序排列

求q次查询之后字符串的结果

思路

训练赛的时候这题没想出来怎么写,下来查了查题解。然后分析一波,终于理解了。

就是建立26棵线段树,对应每个字母,负责记录每个字母出现的位置。

每个T内ans记录的就是该字母出现的位置,而cnt统计我们要操作的那部分的那个字母出现了几次,

然后因为要对那个区间更新,所以对这个区间可以先用0覆盖,然后再根据cnt来重新更新(其实是计数排序)

几个需要注意的地方:

1.因为有用0覆盖的操作,lazy标记应该达成-1

2.update函数应该用赋值,而不是直接套用区间更新的模板在原节点的基础上加减

代码

#include<iostream>
#include<cmath>
#include<stack>
#include<map>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#include<bits/stdc++.h> 
#define Endl "\n"
typedef long long ll;
const int maxn=1e5+5;
const int mod=1e9+7;
using namespace std;


char s[maxn];
inline ll read()
{
	ll x = 0, f = 1; char ch; ch = getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
struct sgt{
	int ans[maxn<<2],lazy[maxn<<2];
	
	void PushUp(int rt)
	{
    	ans[rt]=ans[rt<<1]+ans[rt<<1|1];
    	return ;
	}
	
	void PushDown(int rt,int ln,int rn)//ln表示左子树元素结点个数,rn表示右子树结点个数
	{
    	if (lazy[rt]!=-1)
    	{
    	    lazy[rt<<1]=lazy[rt];
    	    lazy[rt<<1|1]=lazy[rt];
    	    ans[rt<<1]=lazy[rt]*ln;
    	    ans[rt<<1|1]=lazy[rt]*rn;
    	    lazy[rt]=-1;
    	}
	}

	void Update(int L,int R,int C,int l,int r,int rt)
	{
    	if (L<=l&&r<=R)
    	{
        	ans[rt]=C*(r-l+1);
        	lazy[rt]=C;
        	return;
    	}
    	int mid=(l+r)>>1;
    	int left=rt<<1;
		int right=left|1;
    	PushDown(rt,mid-l+1,r-mid);
    	if (L<=mid) Update(L,R,C,l,mid,left);
    	if (R>mid) Update(L,R,C,mid+1,r,right);
    	PushUp(rt);
}

//查询 
	ll Query(int L,int R,int l,int r,int rt)
	{
    	if (L<=l&&r<=R)
    	    return ans[rt];
    	int mid=(l+r)>>1;
    	int left=rt<<1;
		int right=left|1;
    	PushDown(rt,mid-l+1,r-mid);//若更新只有点更新,不需要这句
    	ll ANS=0;
    	if (L<=mid) ANS+=Query(L,R,l,mid,left);
    	if (R>mid) ANS+=Query(L,R,mid+1,r,right);
    	PushUp(rt);
    	return ANS;
	} 

	
};
struct sgt T[26];

int cnt[26];

int n,q;
int main()
{
	n=read(),q=read();
	scanf("%s",s+1);
	for(int i=1;i<=n;i++)
	{
		T[s[i]-'a'].Update(i,i,1,1,n,1);
	}
	while(q--)
	{
		int a,b,key;
		a=read(),b=read(),key=read();
		if(key)
		{
			for(int i=0;i<26;i++)
				cnt[i]=T[i].Query(a,b,1,n,1);
	//			  cout<<"------test1----"<<endl;
      //     for(int i=0;i<26;i++)
       //     	cout<<cnt[i]<<" ";
        //    cout<<endl;
			for(int i=0;i<26;i++)
				T[i].Update(a,b,0,1,n,1);
			int x=a;
			for(int i=0;i<26;i++)
				if(cnt[i]!=0)
				{
					T[i].Update(x,x+cnt[i]-1,1,1,n,1);
					x+=cnt[i];
				}
		}
		else
		{
			for(int i=0;i<26;i++)
				cnt[i]=T[i].Query(a,b,1,n,1);
	//			      cout<<"------test2----"<<endl;
     //      for(int i=0;i<26;i++)
       //     	cout<<cnt[i]<<" ";
      //      cout<<endl;
			for(int i=0;i<26;i++)
				T[i].Update(a,b,0,1,n,1);
			int x=a;
			for(int i=25;i>=0;i--)
				if(cnt[i]!=0)
				{
					T[i].Update(x,x+cnt[i]-1,1,1,n,1);
					x+=cnt[i];
				}
		}
	//	cout<<q<<Endl;
	}
//	cout<<"here1"<<Endl;
	for(int i=1;i<=n;i++)
		for(int j=0;j<26;j++)
			if(T[j].Query(i,i,1,n,1))
				printf("%c",j+'a');
//	cout<<"here2"<<Endl;

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值