F. TorCoder
time limit per test3 seconds
memory limit per test256 megabytes
inputinput.txt
outputoutput.txt
A boy named Leo doesn't miss a single TorCoder contest round.
On the last TorCoder round number 100666 Leo stumbled over the
following problem. He was given a string s, consisting of n
lowercase English letters, and m queries. Each query is
characterised by a pair of integers li, ri (1 ≤ li ≤ ri ≤ n).
We'll consider the letters in the string numbered from 1 to n
from left to right, that is, s = s1s2... sn.
After each query he must swap letters with indexes from li to
ri inclusive in string s so as to make substring (li, ri) a
palindrome. If there are multiple such letter permutations,
you should choose the one where string (li, ri) will be
lexicographically minimum. If no such permutation exists,
you should ignore the query (that is, not change string s).
Everybody knows that on TorCoder rounds input line and array
size limits never exceed 60, so Leo solved this problem easily.
Your task is to solve the problem on a little bit larger limits.
Given string s and m queries, print the string that results
after applying all m queries to string s.
Input
The first input line contains two integers n and m (1 ≤ n, m ≤ 105)
— the string length and the number of the queries.
The second line contains string s, consisting of n lowercase
Latin letters.
Each of the next m lines contains a pair of integers li, ri
(1 ≤ li ≤ ri ≤ n) — a query to apply to the string.
Output
In a single line print the result of applying m queries to
string s. Print the queries in the order in which they are
given in the input.
Examples:
7 2
aabcbaa
1 3
5 7
abacaba
3 2
abc
1 2
2 3
abc
10 3
rrrrrrrrrr
2 8
5 8
3 10
rrrrrrrrrr
Note
A substring (li, ri) 1 ≤ li ≤ ri ≤ n) of string s = s1s2... sn
of length n is a sequence of characters slisli + 1...sri.
A string is a palindrome, if it reads the same from left to right
and from right to left.
String x1x2... xp is lexicographically smaller than string
y1y2... yq,if either p < q and x1 = y1, x2 = y2, ... , xp = yp,
or exists such number r (r < p, r < q), that x1 = y1, x2 = y2
, ... , xr = yr and xr + 1 < yr + 1.
题意:
给定一个长为n的字符串。
有m次操作,每次操作将[l,r]这些位置的字符进行重排,得到字典序最小的回文字符串,如果无法操作就不进行。
求m次操作后的字符串。
思路:
说到底,还是在区间上进行修改,所以考虑线段树?
下面26的思路一点都不好,开一颗线段树,节点上
用二维数组来维护不同字母区间内出现的次数。
线段树,有些地方不注意还是很容易写错的,尤其是
那几个区间边界的表示,例如,区间更新时add要乘以
该节点覆盖区间的长度,在我的代码上这个长度等于
Right[p]-Left[p]+1,而不是R-L+1(这是要修改的区间)。
这一个问题,改了一天?
26颗线段树 维护区间 每个字母的出现次数
为了使得字典序最小,贪心的放进去。
PS:
注意第三组样例情况;
懒标记无效状态不能再用零(实在想用就用其他数表示树节点
的0权值,简单一点直接将懒标记-1作为无效)。
代码实现:(二维节点)build本题中可有可无
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<cstdio>
#include<algorithm>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
const int N=4*2e5+100;
const int M=4e4+100;
int Left[N],Right[N],dat[N][26],add[N][26];
string str;
void build(int p,int l,int r) {
Left[p]=l;
Right[p]=r;
if(l==r) {
for(int i=0; i<26; i++) {
if(i==int(str[l]-'a'))dat[p][i]=1;
else dat[p][i]=0;
add[p][i]=-1;
}
return ;
}
int mid=(l+r)/2;
build(2*p,l,mid);
build(2*p+1,mid+1,r);
for(int i=0; i<26; i++) {
dat[p][i]=dat[p*2][i]+dat[2*p+1][i];
add[p][i]=-1;
}
}
void spread(int p,int zimu) {
if(add[p][zimu]!=-1) {
int mid=(Left[p]+Right[p])/2;
dat[p*2][zimu]=add[p][zimu]*(mid-Left[p]+1);
dat[p*2+1][zimu]=add[p][zimu]*(Right[p]-mid);
add[2*p][zimu]=add[p][zimu];
add[2*p+1][zimu]=add[p][zimu];
add[p][zimu]=-1;
}
}
void change(int p,int l,int r,int v,int zimu) {
if(l<=Left[p]&&r>=Right[p]) {
dat[p][zimu]=(Right[p]-Left[p]+1)*v;
add[p][zimu]=v;
return ;
}
spread(p,zimu);
int mid=(Left[p]+Right[p])/2;
if(l<=mid)change(2*p,l,r,v,zimu);
if(r>mid)change(2*p+1,l,r,v,zimu);
dat[p][zimu]=dat[p*2][zimu]+dat[2*p+1][zimu];
}
int query(int p,int l,int r,int zimu) {
if(l<=Left[p]&&r>=Right[p]) return dat[p][zimu];
spread(p,zimu);
int mid=(Left[p]+Right[p])/2,val=0;
if(l<=mid)val+=query(2*p,l,r,zimu);
if(r>mid)val+=query(2*p+1,l,r,zimu);
return val;
}
int num[N];
int main() {
#ifdef ONLINE_JUDGE
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
#endif
int n,m;
while(cin>>n>>m) {
cin>>str;
for(int i=n-1; i>=0; i--)str[i+1]=str[i];
build(1,1,n);
while(m--) {
int x,y,tmp=0;
cin>>x>>y;
int p=x,q=y;
for(int i=0; i<=25; i++) {
num[i]=query(1,x,y,i);
if(num[i]&1)tmp++;
}
if((y-x+1&1)&&tmp!=1)continue;
if((y-x+1)%2==0&&tmp!=0)continue;
for(int i=0; i<26; i++) {
change(1,x,y,0,i);
}
for(int i=0; i<26; i++) {
if(num[i]&1) {
change(1,(x + y)/2,(x + y)/2,1,i);
num[i] -=1;
}
if(num[i]) {
change(1,p,p+num[i]/2-1,1,i);
change(1,q-num[i]/2+1,q,1,i);
p=p+num[i]/2;
q=q-num[i]/2;
}
}
}
for(int i=1; i<=n; i++) {
for(int j=0; j<26; j++) {
if(query(1,i,i,j)) {
cout<<char('a'+j);
}
}
}
cout<<endl;
}
return 0;
}
代码实现(26棵线段树):
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<cstdio>
#include<algorithm>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
const int N=4*2e5+100;
const int M=4e4+100;
//封装线段树
//节省空间,去掉Left、Right数组
//用传参数表示Left、Right
struct segtree {
int val[N],add[N];
void clear() {
memset(val,0,sizeof(val));
memset(add,-1,sizeof(add));
}
void spread(int p,int Left,int Right) {
if(add[p]!=-1) {
int mid=(Left+Right)/2;
val[2*p]=add[p]*(mid-Left+1);
val[2*p+1]=add[p]*(Right-mid);
add[2*p+1]=add[2*p]=add[p];
add[p]=-1;
}
}
void change(int p,int l,int r,int v,int Left,int Right) {
if(l<=Left&&r>=Right) {
val[p]=v*(Right-Left+1);
add[p]=v;
return ;
}
spread(p,Left,Right);
int mid=(Left+Right)/2;
if(l<=mid)change(2*p,l,r,v,Left,mid);
if(r>mid)change(2*p+1,l,r,v,mid+1,Right);
val[p]=val[2*p]+val[2*p+1];
}
int query(int p,int l,int r,int Left,int Right) {
if(l<=Left&&r>=Right) {
return val[p];
}
spread(p,Left,Right);
int mid=(Left+Right)/2;
int val1,val2;
val1=val2=0;
if(l<=mid)val1=query(2*p,l,r,Left,mid);
if(r>mid)val2=query(2*p+1,l,r,mid+1,Right);
return val1+val2;
}
} arr[27];
int num[28];
//偶数区间修改
void esol(int l,int r,int cnt,int n) {
if(cnt==0) {
for(int i=0; i<26; i++) { //清空原来的
arr[i].change(1,l,r,0,1,n);
}
int p=l,q=r;
for(int i=0; i<26; i++) {
if(num[i]) {
arr[i].change(1,p,p+num[i]/2-1,1,1,n);
arr[i].change(1,q-num[i]/2+1,q,1,1,n);
p=p+num[i]/2;
q=q-num[i]/2;
}
}
}
}
//奇数区间修改
void osol(int l,int r,int cnt,int n) {
if(cnt==1) {
int p=l,q=r;
for(int i=0; i<26; i++) {
arr[i].change(1,l,r,0,1,n);
}
for(int i=0; i<26; i++) {
if(num[i]==0)continue;
if(num[i]&1) {
arr[i].change(1,(l+r)/2,(l+r)/2,1,1,n);
num[i]-=1;
}
arr[i].change(1,p,p+num[i]/2-1,1,1,n);
arr[i].change(1,q-num[i]/2+1,q,1,1,n);
p=p+num[i]/2;
q=q-num[i]/2;
}
}
}
int main() {
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
int n,m,l,r;
string str;
while(cin>>n>>m) {
cin>>str;
memset(num,0,sizeof(num));
for(int i=0; i<26; i++) arr[i].clear();
for(int i=n-1; i>=0; i--) str[i+1]=str[i];
for(int i=1; i<=n; i++)
arr[str[i]-'a'].change(1,i,i,1,1,n);
while(m--) {
cin>>l>>r;
int tmp=0;
for(int i=0; i<26; i++) {
num[i]=arr[i].query(1,l,r,1,n);
if(num[i]&1)tmp++;
}
if((l-r+1)%2==0) esol(l,r,tmp,n);
else osol(l,r,tmp,n);
}
for(int i=1; i<=n; i++) {
for(int j=0; j<26; j++) {
if(arr[j].query(1,i,i,1,n)) {
cout<<char('a'+j);
}
}
}
cout<<endl;
}
return 0;
}
THE END;