Codeforces 240F : TorCoder

【大意】
给出一个长度为n 的字符串,以及m 个操作。每个操作给出一个区间[l; r],你需要将[l; r]
中的字符重排使得这个子串成为一个回文串且字典序最小。如果某个操作不可能排出回文串,
则直接无视这个操作。你需要输出经过m 次操作后的字符串。

【解答】
用26棵线段树对于每一种字符分开来维护
线段树节点维护区间字符出现的次数,分别维护他们的位置

判断一次操作是否合法:
对于每一个字符, 查询区间内该元素的个数,若出现两个字符出现的次数同为奇数,或者区间长度为偶数并且有字符出现的次数为奇数,则该操作不合法。

进行一次操作:
对于单个字符,由于操作结束之后位置一定是在两侧连续的(不考虑出现次数为奇数时中间的那一个字符),用线段树区间赋值标记即可。
最后单点修改中间的那一个字符

注意常数因子对程序运行效率的影响

#include <bits/stdc++.h>
#define N 100050

#define mid ((l+r)>>1)
#define ls l,mid,t<<1
#define rs mid+1,r,t<<1^1

using namespace std;

int n,m,v,ll,rr,ret,cur[30];
char s[N];
inline int rd() {int r;scanf("%d",&r);return r;}
struct Node{int sum,siz;}tr[4*N],id;
Node operator+(Node p1, Node p2) {
    Node ret = id;
    ret.sum = p1.sum + p2.sum;
    ret.siz = p1.siz + p2.siz;
    return ret;
}
struct Segment_Tree{
    Node tr[4*N];
    int a[N],ag[4*N];

    Node build(int l,int r,int t) {
        ag[t] = -1;
        return tr[t] = l==r ? (Node){a[l],1} : build(ls) + build(rs);
    }

    void push_down(int t) {
        if (ag[t] == -1) return ;
        tr[t<<1].sum = ag[t] * tr[t<<1].siz;
        tr[t<<1^1].sum = ag[t] * tr[t<<1^1].siz;
        ag[t<<1] = ag[t<<1^1] = ag[t];
        ag[t] = -1;
    }

    void update(int l,int r,int t) {
        if (l > rr || r < ll) return ;
        if (ag[t] == v) return ;
        if (l >= ll && r <= rr) {
            ag[t] = v;
            tr[t].sum = v * tr[t].siz;
            return ;
        }
        push_down(t);
        update(ls); update(rs);
        tr[t] = tr[t<<1] + tr[t<<1^1];
    }

    void query(int l,int r,int t) {
        if (l > rr || r < ll) return ;
        if (ag[t] != -1) return ret += ag[t] * (min(rr,r)-max(ll,l)+1), (void)0;
        if (l >= ll && r <= rr) return ret += tr[t].sum, (void)0;
        push_down(t);
        query(ls); query(rs);
    }

}T[27];

int main() {
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    n = rd(), m = rd();
    scanf("%s",s+1);

    for (int _=1;_<=n;_++) T[s[_]-'a'].a[_] = 1;
    for (int _=0;_<26;_++) T[_].build(1,n,1);
    while (m--) {
        int l = rd(), r = rd(), d = -1, flag = 0;
        for (int _=0;_<26;_++) {
            ret = 0, ll = l, rr = r;
            T[_].query(1,n,1);
            cur[_] = ret / 2;
            if (ret&1) {
                if (d == -1 && (r-l+1)%2 == 1) d = _;else {flag = 1; break;}
            }
        }

        if (flag) continue;
        for (int _=1;_<26;_++) cur[_] += cur[_-1];
        for (int _=0;_<26;_++) {
            v = 1;
            if (!_) ll = l; else ll = l+cur[_-1];
            rr = l+cur[_]-1;
            if (ll <= rr) T[_].update(1,n,1);

            v = 0, rr = ll-1, ll = l;
            if (ll <= rr) T[_].update(1,n,1);

            v = 1;
            ll = r-cur[_]+1;
            if (!_) rr = r; else rr = r-cur[_-1];
            if (ll <= rr) T[_].update(1,n,1);

            v = 0, ll = rr+1, rr = r;
            if (ll <= rr) T[_].update(1,n,1);

            v = 0, ll = l+cur[_], rr = r-cur[_];
            if (ll <= rr) T[_].update(1,n,1);
        }

        if (~d) v = 1, ll = rr = (l+r)>>1, T[d].update(1,n,1);

    }

    for (int _=1;_<=n;_++) {
        ret = 0;
        for (int i=0;i<26;i++) {
            ll = rr = _, T[i].query(1,n,1);
            if (ret) {putchar('a'+i);break;}
        }
    }
    puts("");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值