【并查集+平衡树启发式合并】LibreOJ β Round #2[DP一般看规律]题解

ps:最近都在A一些数据结构题,感觉又要变笨了QAQ(本来就很笨)

题目概述

给出一个长度为n的序列和m个操作,每个操作会把所有x改成y,每次操作过后输出min{j-i|a[i]=a[j]}。

解题报告

我们会发现这样一个性质:一种数字变成另外一种数字,就不可能单独变回来了。所以一种数字一旦消失是撤销不了的(其他数字变成该数字不算)。那么也就是说我们可以利用平衡树启发式合并每种数字(先离散),然后查询一下最小距离即可。
不知道是不是因为打了SBT,我竟然rank4了!

示例程序

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100000,maxm=100000,maxt=maxn+2*maxm,MAXINT=((1<<30)-1)*2+1;

int n,m,a[maxn+5],b[maxt+5],x[maxm+5],y[maxm+5],MIN;
struct node
{
    node* son[2];int si,key;
    node(int k,node* p,int s=1) {si=s;key=k;son[0]=son[1]=p;}
    bool cmp(int k) {return k>=key;}
    void Pushup() {si=son[0]->si+1+son[1]->si;}
};
node nil(0,&nil,0);
typedef node* P_node;
P_node null=&nil,ro[maxt+5];
void Rotate(P_node &p,bool d)
{
    P_node t=p->son[d^1];p->son[d^1]=t->son[d];t->son[d]=p;
    p->Pushup();t->Pushup();p=t;
}
void Maintain(P_node &p,bool fl)
{
    P_node L=p->son[0],R=p->son[1]; 
    if (!fl)
        if (L->son[0]->si>R->si) Rotate(p,1); else
        if (L->son[1]->si>R->si) Rotate(p->son[0],0),Rotate(p,1); else
        return;
    else
        if (R->son[1]->si>L->si) Rotate(p,0); else
        if (R->son[0]->si>L->si) Rotate(p->son[1],1),Rotate(p,0); else
        return;
    Maintain(p->son[0],0);Maintain(p->son[0],1);
    Maintain(p->son[1],0);Maintain(p->son[1],1);
    Maintain(p,0);Maintain(p,1);
}
void Insert(P_node &p,int k)
{
    if (p==null) {p=new node(k,null);return;}
    int d=p->cmp(k);Insert(p->son[d],k);
    p->Pushup();Maintain(p,d);
}
int getpre(P_node p,int k)
{
    if (p==null) return -MAXINT;
    if (k>p->key) return max(p->key,getpre(p->son[1],k)); else
    return getpre(p->son[0],k);
}
int getsuf(P_node p,int k)
{
    if (p==null) return MAXINT;
    if (k<p->key) return min(p->key,getsuf(p->son[0],k)); else
    return getsuf(p->son[1],k);
}
int Ask(P_node A,P_node B)
{
    if (B==null) return MAXINT;
    int now=MAXINT,pre=getpre(A,B->key),suf=getsuf(A,B->key);
    if (pre!=-MAXINT) now=min(now,B->key-pre);
    if (suf!=MAXINT) now=min(now,suf-B->key);
    return min(now,min(Ask(A,B->son[0]),Ask(A,B->son[1])));
}
void Join(P_node &A,P_node B)
{
    if (B==null) return;Insert(A,B->key);
    Join(A,B->son[0]);Join(A,B->son[1]);
}
int Find(int x)
{
    int L=1,R=b[0];
    while (L<=R)
    {
        int mid=L+(R-L>>1);
        if (x==b[mid]) return mid;
        if (x<b[mid]) R=mid-1; else L=mid+1;
    }
}

int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]),b[++b[0]]=a[i];
    for (int i=1;i<=m;i++) scanf("%d%d",&x[i],&y[i]),b[++b[0]]=x[i],b[++b[0]]=y[i];
    sort(b+1,b+b[0]+1);
    b[0]=1;for (int i=2;i<=n+2*m;i++) if (b[i]>b[i-1]) b[++b[0]]=b[i];
    for (int i=1;i<=b[0];i++) ro[i]=null;
    for (int i=1;i<=n;i++) Insert(ro[Find(a[i])],i);
    MIN=MAXINT;for (int i=1;i<=b[0];i++) MIN=min(MIN,Ask(ro[i],ro[i]));
    for (int i=1;i<=m;i++)
    {
        int A=Find(x[i]),B=Find(y[i]);
        if (A==B) {printf("%d\n",MIN);continue;}
        if (ro[A]->si>ro[B]->si)
        {
            MIN=min(MIN,Ask(ro[A],ro[B]));
            Join(ro[A],ro[B]);ro[B]=ro[A];ro[A]=null;
        } else
        {
            MIN=min(MIN,Ask(ro[B],ro[A]));
            Join(ro[B],ro[A]);ro[A]=null;
        }
        printf("%d\n",MIN);
    }
    return 0;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值