Codeforces Round #145 (Div. 1) 240F TorCoder

题意: 给一个字符串,有m次操作。每次操作子串[l,r],如果子串能变成回文就把它变成字典序最小的回文,否则不操作。输出最后的字符串。


思路: 如果你知道一个字符串有几个a组成几个b组成...,并且它能够组成回文,那么你就能迅速写出它字典序最小的回文(a先放两边再b这样下去)。所以我们只要用线段树统计好字符串里26个字母的个数就能得出回文,并且按这个回文更新线段树。


代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <climits>
#include <numeric>
#include <vector>

#define N 100010

int n;
char buf[N];

struct segment{
	int l,r,mark,cnt[26];
	segment(){memset(cnt,0,sizeof(cnt));mark=-1;}
};

class segTree{
#define lson rt<<1
#define rson rt<<1|1
#define rtl seg[rt].l
#define rtr seg[rt].r
private:
	segment seg[N<<2];
public:
	void setValue(int c,int rt)
	{
		memset(seg[rt].cnt,0,sizeof(seg[rt].cnt));
		seg[rt].cnt[c]=rtr-rtl+1;
	}
	void pushup(int rt)
	{
		for(int i=0;i<26;i++)
			seg[rt].cnt[i]=seg[lson].cnt[i]+seg[rson].cnt[i];
	}
	void pushdown(int rt)
	{
		if(seg[rt].mark!=-1){
			setValue(seg[rt].mark,lson);
			setValue(seg[rt].mark,rson);
			seg[lson].mark=seg[rt].mark;
			seg[rson].mark=seg[rt].mark;
			seg[rt].mark=-1;
		}
	}
	void update(int c,int L,int R,int rt)
	{
		if(L<=rtl && rtr <=R){
			seg[rt].mark=c;
			setValue(c,rt);
			return ;
		}
		pushdown(rt);
		int mid=(rtl+rtr)>>1;
		if(L<=mid) update(c,L,R,lson);
		if(R>mid) update(c,L,R,rson);
		pushup(rt);
	}
	void query(int cnt[],int L,int R,int rt)
	{
		if(L<=rtl && rtr <=R){
			for(int i=0;i<26;i++)
				cnt[i]+=seg[rt].cnt[i];
			return ;
		}
		int mid=(rtl+rtr)>>1;
		pushdown(rt);
		if(L<=mid) query(cnt,L,R,lson);
		if(R>mid) query(cnt,L,R,rson);
	}
	void build(int l,int r,int rt)
	{
		rtl=l,rtr=r;
		if(l==r){
			seg[rt].cnt[buf[l-1]-'a']=1;
			return ;
		}
		int mid=(l+r)>>1;
		build(l,mid,lson);
		build(mid+1,r,rson);
		pushup(rt);
	}
	void transform(char ans[],int rt)
	{
		if(rtl==rtr){
			for(int i=0;i<26;i++)
				if(seg[rt].cnt[i]){
					ans[rtl-1]='a'+i;
					break;
				}
				return ;
		}
		pushdown(rt);
		transform(ans,lson);
		transform(ans,rson);
	}
}T;

bool check(int l,int r,int cnt[])
{
	int i,oddsum=0;
	for(i=0;i<26;i++)
		if(cnt[i]%2) oddsum++;
	if(oddsum>1) return false;
	if(oddsum!=(r-l+1)%2) return false;
	return true;
}
void solve(int L,int R)
{
	int i,cnt[26]={0},oddpos=-1,l,r,_l,_r;
	T.query(cnt,L,R,1);
	if(!check(L,R,cnt)) return ;
	for(i=0;i<26;i++){
		if(cnt[i]%2){
			oddpos=i;
			break;
		}
	}
	if(oddpos!=-1) 
		cnt[oddpos]--;
	l=L , _r=R;
    for(i=0;i<26;i++){
		cnt[i]/=2;
		if(cnt[i]){
			r=cnt[i]+l-1;
			_l=-cnt[i]+_r+1;
			T.update(i,l,r,1);
			T.update(i,_l,_r,1);
			l=r+1;
			_r=_l-1;
		}
	}
	if(oddpos!=-1)
		T.update(oddpos,l,_r,1);
}

int main()
{
#ifdef 	ONLINE_JUDGE
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif
	int m,l,r;
	scanf("%d %d",&n,&m);
	scanf("%s",buf);
	T.build(1,n,1);
	while(m--){
		scanf("%d %d",&l,&r);
		solve(l,r);
	}
	T.transform(buf,1);
	printf("%s\n",buf);
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值