平衡树+并查集 string

本文介绍了一种使用Treap(伸展树)和并查集解决特定字符串操作问题的方法。通过构建Treap来高效处理字符串的反转操作,并利用并查集维护字符串中未知字符的合法状态,最终确定第k大的排列方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题干去内网找。。
开始读错题了,只要所有的操作都完成后与之前的无区别就好了。
这个反转用无旋treap(splay都行)搞一下,dfs出最后的序列。
然后,初始序列和最终序列的同一位上为同一个字母。考虑两种情况
1.两个‘?’,用并查集搞进一个联通块即可。
2.一个字母,一个‘?’。那这个?所在联通块里所有?都得是那个字母。
把这里处理完了之后就是要去处理第k大。
只有剩下不多的联通块里的问号可以a~z随意取。而且只有联通块里最前面的一个?的取值决定着答案。
所以并查集时把最前面的?搞成父亲。统计出每个联通块第一个问号的顺序,然后搞一搞。具体怎么搞。。。某一个?要把字典序+1,那么总的排名变化,就是26^这个问号之后问号个数。稍微自己想想。。。不太好解释。通过这种方式能确定每一个?是哪个字母。。
然后输出即可。

#pragma GCC optimize("O3")
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 5*100005
#define ll long long
using namespace std;
int read()
{
    int sum=0,f=1;char x=getchar();
    while(x<'0'||x>'9'){if(x=='-')f=-1;x=getchar();}
    while(x>='0'&&x<='9'){sum=(sum<<1)+(sum<<3)+x-'0';x=getchar();}
    return sum*f;
}
int n,m,tot=26,cnt,a[N],b[N],f[N],vis[N],wz[N],hh[N];char s[N];
ll xp[20],k;
namespace Treap
{
    struct treap
    {
        treap* lc;treap* rc;
        int sum,l,h,id;
        treap(){lc=rc=NULL;id=rand();sum=l=h=0;}
        inline void up(){sum=lc->sum+rc->sum+1;}
    }*null=new treap(),*root=null,*zhan[N];
    typedef pair<treap*,treap*> D;
    void down(treap* &f)
    {
        if(f==null)return;
        if(f->l)
        {
            f->l^=1;
            if(f->lc!=null)f->lc->l^=1;
            if(f->rc!=null)f->rc->l^=1;
            swap(f->lc,f->rc);
        }
    }
    D split(treap* f,int k)
    {
        if(f==null)return D(null,null);
        D y;down(f);
        if(f->lc->sum>=k){y=split(f->lc,k);f->lc=y.second;f->up();y.second=f;}
        else{y=split(f->rc,k-f->lc->sum-1);f->rc=y.first;f->up();y.first=f;}
        return y;
    }
    treap* merge(treap* a,treap* b)
    {
        if(a==null)return b;
        if(b==null)return a;
        down(a);down(b);
        if(a->id<b->id){a->rc=merge(a->rc,b);a->up();return a;}
        else {b->lc=merge(a,b->lc);b->up();return b;}
    }
    treap* newtreap(int k)
    {
        treap* o=new treap();
        o->lc=o->rc=null;
        o->sum=1;o->h=k;
        return o;
    }
    void zhuan(int l,int r)
    {
        D y=split(root,l-1);
        D x=split(y.second,r-l+1);
        if(x.first!=null)x.first->l^=1;
        root=merge(y.first,merge(x.first,x.second));
    }
    treap* build()
    {
        int x,p=0;treap *h,*las;
        for(int i=1;i<=n;i++)
        {
            if(s[i]=='?')x=a[i]=++tot;
            else a[i]=x=s[i]-'a';
            h=newtreap(x);las=null;
            while(p&&zhan[p]->id>h->id)
            {
                zhan[p]->up();las=zhan[p];zhan[p--]=null;
            }
            if(p)zhan[p]->rc=h;
            h->lc=las;zhan[++p]=h;
        }
        while(p)zhan[p--]->up();
        return zhan[1];
    }
    void dfs(treap* x)
    {
        if(x==null)return;
        down(x);
        if(x->lc!=null)dfs(x->lc);
        b[++cnt]=x->h;
        if(x->rc!=null)dfs(x->rc);
    }
}
using namespace Treap;
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
int main()
{
    n=read();m=read();scanf("%lld",&k);
    null->lc=null->rc=null;
    scanf("%s",s+1);
    root=build();
    int l,r;
    while(m--){l=read();r=read();zhuan(l,r);}
    dfs(root);
    for(int i=27;i<=tot;i++)f[i]=i;
    for(int i=1;i<=n;i++)
    {
        if(b[i]>26)
        {
            if(a[i]>26)
            {
                int fx=find(b[i]),fy=find(a[i]);
                if(fx^fy)
                {
                    if(fx<fy)f[fy]=fx;else f[fx]=fy;
                    if(vis[fx])vis[fy]=1,hh[fy]=hh[fx];
                    if(vis[fy])vis[fx]=1,hh[fx]=hh[fy];
                }
            }
            else{int fx=find(b[i]);vis[fx]=1;hh[fx]=a[i];}
        }
        else
            if(a[i]>26){int fx=find(a[i]);vis[fx]=1;hh[fx]=b[i];}
    }
    cnt=0;
    for(int i=27;i<=tot;i++)
    {
        int fx=find(i);
        if(vis[fx])continue;
        if(fx==i){wz[++cnt]=fx;}
    }
    xp[0]=1;int i=1;
    while(1)
    {
        xp[i]=xp[i-1]*26ll;
        if(xp[i]>=k)break;
        i++;
    }
    for(int j=1;j<=cnt-i;j++)hh[wz[j]]=0;
    for(;i>0;i--)
        for(ll j=1;j<=26;j++)
            if(j*xp[i-1]>=k)
            {
                hh[wz[cnt-i+1]]=j-1;
                k-=(j-1)*xp[i-1];
                break;
            }
    for(int i=1;i<=n;i++)
        if(b[i]>26)s[i]=hh[find(b[i])]+'a';
        else s[i]=b[i]+'a';
    printf("%s",s+1);
}   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值