平衡树+并查集 string

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

题干去内网找。。
开始读错题了,只要所有的操作都完成后与之前的无区别就好了。
这个反转用无旋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);
}   
想要使用树结构实现成绩的快速查询,可选用合适的树结构,如平衡的二叉搜索树(像红黑树),并以下述步骤操作为主。 ### 借助红黑树开展成绩的快速查询 红黑树属于自平衡的二叉搜索树,其能够保证在插入、删除与查找操作时拥有对数级别的时间复杂度$O(\log n)$,对于动态数据集极为适用。在 Java 里,标准库提供了基于红黑树实现的 `TreeMap`,能用来存储学生成绩,学生的姓名可作为键(Key),而成绩则作为值(Value),示例代码如下: ```java import java.util.TreeMap; public class ScoreQuery { public static void main(String[] args) { // 创建一个基于红黑树的 TreeMap 来存储学生成绩 TreeMap<String, Integer> scoreMap = new TreeMap<>(); // 添加学生成绩 scoreMap.put("Alice", 85); scoreMap.put("Bob", 90); scoreMap.put("Charlie", 78); // 查询学生成绩 String studentName = "Bob"; if (scoreMap.containsKey(studentName)) { int score = scoreMap.get(studentName); System.out.println(studentName + " 的成绩是: " + score); } else { System.out.println("未找到 " + studentName + " 的成绩记录。"); } } } ``` ### 手动实现一个二叉搜索树用于成绩查询 如果需要手动实现一个二叉搜索树来查询成绩,可参考如下的示例代码: ```java public class BinarySearchTree { static class TreeNode { public String studentName; public int score; public TreeNode left; public TreeNode right; public TreeNode(String studentName, int score) { this.studentName = studentName; this.score = score; } } public TreeNode root; // 插入节点 public void insert(String studentName, int score) { root = insertRec(root, studentName, score); } private TreeNode insertRec(TreeNode root, String studentName, int score) { if (root == null) { return new TreeNode(studentName, score); } if (studentName.compareTo(root.studentName) < 0) { root.left = insertRec(root.left, studentName, score); } else if (studentName.compareTo(root.studentName) > 0) { root.right = insertRec(root.right, studentName, score); } return root; } // 搜索节点 public int search(String studentName) { return searchRec(root, studentName); } private int searchRec(TreeNode root, String studentName) { if (root == null || root.studentName.equals(studentName)) { return root != null ? root.score : -1; } if (studentName.compareTo(root.studentName) < 0) { return searchRec(root.left, studentName); } return searchRec(root.right, studentName); } public static void main(String[] args) { BinarySearchTree bst = new BinarySearchTree(); // 插入成绩 bst.insert("Alice", 85); bst.insert("Bob", 90); bst.insert("Charlie", 78); // 查询成绩 String studentName = "Bob"; int score = bst.search(studentName); if (score != -1) { System.out.println(studentName + " 的成绩是: " + score); } else { System.out.println("未找到 " + studentName + " 的成绩记录。"); } } } ``` 在上述两种方法里,使用 `TreeMap` 是较为简便的做法,因其内部已经实现了红黑树的平衡操作;而手动实现二叉搜索树则能让开发者对数据结构有更深入的理解,但需要自行处理树的平衡问题,否则在极端情况下性能会退化到 $O(n)$ 。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值