习题:JM的西伯利亚特快专递 (线段树)
Posted loney-s
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了习题:JM的西伯利亚特快专递 (线段树)相关的知识,希望对你有一定的参考价值。
题目
今天JM收到了一份来自西伯利亚的特快专递,里面装了一个字符串 (s) ,仅包含小写英文字母。于是JM决定跟qz和寒域爷一起玩一个游戏:
JM手里拿着字符串 (s) ,qz手里拿着字符串 (t) ,寒域爷手里拿着字符串 (u) ,初始时 (t) 和 (u) 均为空。有两种操作:
将字符串 (s) 的首部字符取出,插入字符串 (t) 的尾部。
将字符串 (t) 的尾部字符取出,插入字符串 (u) 的尾部。
JM,qz和寒域爷随意地进行上述两种操作,直到字符串 (s) 和 (t) 均为空时才停止。现在JM想知道,他们能得到的字典序最小的字符串 (u) 是什么。
注意,对于一个字符串 (s) ,其长度为 (n) ,其中字符的下标为 (1, 2, ..., n) ,则我们定义 (s) 的首部字符为 (s_1) ,尾部字符为 (s_n) 。
思路
还算简单的一道题,你直接考虑字符串u的性质就行了,用线段树维护
代码
#include<iostream>
#include<cstring>
#include<deque>
#include<cstdio>
using namespace std;
struct node
{
char c;
int l;
int r;
}tre[400005];
char minn;
char s[100005];
int now;
int lens;
deque<char> t;
deque<char> u;
void build(int l,int r,int k)
{
tre[k].l=l;
tre[k].r=r;
if(l==r)
return;
int mid=(l+r)>>1;
build(l,mid,k<<1);
build(mid+1,r,k<<1|1);
}
void change(int pos,int k,char c)
{
if(tre[k].l>pos||tre[k].r<pos)
return;
if(tre[k].l==tre[k].r)
{
tre[k].c=c;
return;
}
change(pos,k<<1,c);
change(pos,k<<1|1,c);
tre[k].c=min(tre[k<<1].c,tre[k<<1|1].c);
}
char ask(int l,int r,int k)
{
if(tre[k].l>r||tre[k].r<l)
return 'z';
if(l<=tre[k].l&&tre[k].r<=r)
return tre[k].c;
if(tre[k].l==tre[k].r)
return tre[k].c;
return min(ask(l,r,k<<1),ask(l,r,k<<1|1));
}
int main()
{
ios::sync_with_stdio(false);
cin>>s;
lens=strlen(s);
build(1,lens,1);
for(int i=lens;i>=1;i--)
{
s[i]=s[i-1];
change(i,1,s[i]);
}
now=0;
while(now!=lens)
{
minn=ask(now+1,lens,1);
while(!t.empty())
{
if(minn>=t.back())
{
u.push_back(t.back());
t.pop_back();
}
else
break;
}
for(int i=now+1;i<=lens;i++)
{
if(s[i]!=minn)
t.push_back(s[i]);
else
{
now=i;
u.push_back(s[i]);
break;
}
}
}
while(!u.empty())
{
cout<<u.front();
u.pop_front();
}
while(!t.empty())
{
cout<<t.back();
t.pop_back();
}
return 0;
}
以上是关于习题:JM的西伯利亚特快专递 (线段树)的主要内容,如果未能解决你的问题,请参考以下文章