SD provincial team training 2019Day8 of "there is no empty"

There are no empty (busy) ([Ynoi2018] Heaven thereof)

To give you a sequence of length n, a
you need to implement the m operation, there are two operations:
1. the sequence number value of all values of x Y becomes
2. find a location i satisfies ai == x, find a position j satisfy aj == y, such that | ij of | a minimum, and outputs | ij of |

Part points: two points

Consider the whole range is divided into two parts, then these two numbers are either on the left or on the right are, or either side. The first two cases can be recursively solved, while the current recording interval leftmost and rightmost segments x and y, and then counted directly.

#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[100001];
int fir[100001],to[100001],when[100001],nxt[100001],cnt;
inline void solve(int l,int r,int x,int y,int&ans,int&lx,int&ly,int&rx,int&ry);
inline int calc(int num){
    register int tim=0;
    while(fir[num]){
        register int p=fir[num];
        while(when[p]<tim&&nxt[p])p=nxt[p];
        if(when[p]<tim)break;
        if(to[p]==num)break;
        num=to[p];
        tim=when[p];
    }
    return num;
}
int main(){
    freopen("busy.in","r",stdin);
    freopen("busy.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(register int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    register int la=0;
    for(register int mm=1;mm<=m;mm++){
        register int opt,x,y;
        scanf("%d%d%d",&opt,&x,&y);
        x^=la;
        y^=la;
        if(opt==1){
            to[++cnt]=y;
            when[cnt]=mm;
            int p=fir[x];
            if(!p)fir[x]=cnt;
            else{
                while(nxt[p])p=nxt[p];
                nxt[p]=cnt;
            }
        }else if(opt==2){
            register int ans,lx,ly,rx,ry;
            solve(1,n,x,y,ans,lx,ly,rx,ry);
            if(ans==-1)printf("Chtholly\n"),la=0;
            else printf("%d\n",ans),la=ans;
        }
    }
}
inline void solve(int l,int r,int x,int y,int&ans,int&lx,int&ly,int&rx,int&ry){
    if(l==r){
        ans=-1;
        if(calc(a[l])==x)lx=rx=l;
        else lx=rx=-1;
        if(calc(a[r])==y)ly=ry=r;
        else ly=ry=-1;
        if(x==y&&x==calc(a[l])){
            ans=0;
            lx=rx=l;
            ly=ry=r;
        }
        return;
    }
    register int mid=(l+r)/2;
    register int lans,llx,lly,lrx,lry;
    register int rans,rlx,rly,rrx,rry;
    solve(l,mid,x,y,lans,llx,lly,lrx,lry);
    solve(mid+1,r,x,y,rans,rlx,rly,rrx,rry);
    ans=INT_MAX;
    if(lans!=-1&&lans<ans)ans=lans;
    if(rans!=-1&&rans<ans)ans=rans;
    if(llx==-1)lx=rlx;else lx=llx;
    if(lly==-1)ly=rly;else ly=lly;
    if(rrx==-1)rx=lrx;else rx=rrx;
    if(rry==-1)ry=lry;else ry=rry;
    if(lrx!=-1&&rly!=-1)ans=min(ans,rly-lrx);
    if(lry!=-1&&rlx!=-1)ans=min(ans,rlx-lry);
    if(ans==INT_MAX)ans=-1;
}

practice

Root partition.

First, look at the query operation. If we position all the values in ascending order of record, then the direct violence inquiry is \ (sz [the X-] + sz [the y-] \) , where \ (sz [x] \) represents \ (x \) the number of value occurs. Consider how to optimize this great violence. So you can use the root partition idea of it. We set a threshold value \ (Lim \) , with the threshold for discussion.

  1. \ (sz [x], sz [y] \ leq lim \) direct violence inquiry.

  2. \ (sz [x]> lim \) can be pre-out \ (X \) minimum distance from all the other values, and then directly \ (O (1) \) query. Complexity is pretreated \ ({lim} \ ast n O (\ frac {n}) \)

So can \ (lim \) is set to \ (\ sqrt the n-\) , which is obviously the best.

But if added to modify the operation, we can not modify each time after also re-pretreated violence, should a need something to optimize this pretreatment.

Let's look at the revised operation.

First, obviously \ (x, y \) is equivalent to two values, it can be directly exchanged.

  1. \ (sz [x], sz [y] <= lim \) direct violence combined two set positions, such complexity is \ (O (Lim + Lim) \) .

  2. \ (sz [x], sz [y]> = \ lim) direct violence recompose, the pretreatment \ (Y \) to the minimum distance from all other values. Complexity is a reconstructed \ (O (n) \) , but most reconstruction \ (O (\ frac {n } {lim}) \) times, so there is the complexity of the \ (O (n \ AST \ {n-FRAC Lim {}}) \) .

  3. \ (sz [x]> = lim, sz [y] <= lim \) where it is hard to deal with the direct reconstruction nor the combined force is not right. We found that the \ (Y \) incorporated into \ (X \) be shared equally in the position number is \ (O (n) \) a. So we can for each \ (sz> = lim \) to maintain the number of a new set \ (the PSZ \) , and then maintains a \ (ans [A] [B ] \) array representing all \ (sz> = Lim \) number \ (a \) to all the other numbers \ (B \) is removed in \ (the PSZ \) minimum distance inside. Each time \ (y \) merged into \ (x \) , we direct that it be incorporated into \ (psz [x] \) , followed by \ (ans [A] [y ] \) to update \ (ANS [A] [X] \) . This complexity becomes a good read. Such complexity is \ (O (lim + lim) \) a. If the merger \ (psz \) is larger than \ (lim \), The violence reprocessing \ (x \) to the minimum distance from all the other numbers. Such a reconstruction formula \ (O (n) \) , but for up \ (O (\ frac {n } {lim}) \) times, so this complexity is \ (O (n \ ast \ frac n-Lim} {} {) \) . This also ensures size pszpszpsz is always less than \ (Lim \) .

Then the next query on many easy.

  1. \ (sz [x], sz [y] <\ = lim) is still violent combined complexity \ (O (Lim + Lim) \) .

  2. \ (sz [x], sz [y]> = lim \) merger \ (psz [x] \) and \ (PSZ [the y-] \) , plus \ (ans \) in the answer is the real answer. Such complexity is \ (O (Lim + Lim) \) .

  3. \ (sz [x]> = lim, sz [y] <= lim \) similar to the above, no difference.

Therefore, the overall complexity is \ (\ sum_. 1} ^ {n-I = (Lim + \ {n-FRAC Lim {}}) \) , take \ (lim = \ sqrt (n ) \) Optimal.

So it is done.

Code

#include<bits/stdc++.h>
using namespace std;
#define res register int
#define LL long long
#define inf 0x3f3f3f3f
#define eps 1e-10
#define RG register
inline int read() {//快读
    res s=0,ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    return s;
}
inline LL Read() {//快读
    RG LL s=0;
    res ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    return s;
}
inline void swap(res &x,res &y) {//交换两个变量的值,较快
    x^=y^=x^=y;
}
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());//随机数生成器
typedef vector<int> vec;
const int N=1e5+10;
const int BB=500;
namespace MAIN {
    vec v[N];
    int n,m,block,st[N],top,bigx=1,ans[N/BB+10][N],id[N],big[N],tim,a[N],sz[N],psz[N],lastans,ys[N];
    inline int newnode(){           //创建新节点
        return top?st[top--]:++bigx;
    }
    inline void init(const res &B){//初始化,创建新节点时使用
        for(res i=1;i<=n;i++)ans[B][i]=inf;
        for(res i=1,now=inf;i<=n;i++)
            if(id[i]==tim)now=0;
            else ans[B][a[i]]=min(ans[B][a[i]],++now);
        for(res i=n,now=inf;i;i--)
            if(id[i]==tim)now=0;
            else ans[B][a[i]]=min(ans[B][a[i]],++now);
    }
    inline void bigbuild(const res &val){//大于lim时,重构
        big[val]=newnode(),tim++;
        for(res i=1;i<=n;i++)if(a[i]==val)id[i]=tim;
        init(big[val]),ans[big[val]][val]=0;
    }
    inline void rebuild(const res &A,const res &B){//重构
        tim++;
        if(big[A])st[++top]=big[A];
        for(res i=1;i<=n;i++)if(a[i]==A||a[i]==B)id[i]=tim,a[i]=B;
        psz[B]=0,init(big[B]);
    }
    inline void merge(const res &A,const res &B){//合并
        res i=sz[A],j=sz[B]<block?sz[B]:psz[B],k=i+j;
        RG vec tmp=v[B];
        v[B].resize((k<<1)+10);
        while(i&&j)v[B][k--]=(v[A][i]>tmp[j]?v[A][i--]:tmp[j--]);
        while(i)v[B][k--]=v[A][i--];
        while(j)v[B][k--]=tmp[j--];
    }
    inline void modify(res x,res y){
        res A=ys[x],B=ys[y];
        if(A==B||!sz[A])return; //相等或者不存在,无需操作
        if(sz[A]>sz[B])swap(A,B),swap(x,y),ys[y]=0,ys[x]=B;
        else ys[x]=0;
        if(!A||!B)return;
        if(sz[B]<block){            //sz小于lim,暴力合并
            if(sz[A]+sz[B]<block){  //直接做
                merge(A,B);
                for(res i=1;i<=bigx;i++)ans[i][B]=min(ans[i][B],ans[i][A]);
                for(res i=1;i<=sz[A];i++)a[v[A][i]]=B;
            }
            else {
                big[B]=newnode(),tim++;
                for(res i=1;i<=sz[B];i++)id[v[B][i]]=tim;
                for(res i=1;i<=sz[A];i++)id[v[A][i]]=tim,a[v[A][i]]=B;
                init(big[B]);
            }
        }
        else
        if(sz[A]<block){            //sz一大一小
            if(psz[B]+sz[A]<block){ //改为合并psz
                merge(A,B);
                for(res i=1;i<=bigx;i++)ans[i][B]=min(ans[i][B],ans[i][A]);
                for(res i=1;i<=sz[A];i++)a[v[A][i]]=B;
                psz[B]+=sz[A];
            }
            else rebuild(A,B);      //重构
        }
        else rebuild(A,B);
        sz[B]+=sz[A],sz[A]=0;
    }
    inline int merge_ans(const res &A,const res &B){
        res i=1,j=1,sa=sz[A],sb=sz[B],ret=inf;
        if(sz[A]>=block)sa=psz[A];  //大于lim时改为合并psz
        if(sz[B]>=block)sb=psz[B];  //同上
        if(!sa||!sb)return inf;     //不存在则无解,因为调用时是取min,所以可以return无穷大
        while(i<=sa&&j<=sb)ret=min(ret,v[A][i]<v[B][j]?v[B][j]-v[A][i++]:v[A][i]-v[B][j++]);//合并
        if(i<=sa)ret=min(ret,abs(v[A][i]-v[B][sb]));//求答案
        if(j<=sb)ret=min(ret,abs(v[A][sa]-v[B][j]));//同上
        return ret;
    }
    inline int query(res x,res y){
        res A=ys[x],B=ys[y];
        if(A==B)return sz[A]?0:-1;                  //相等特判
        if(!sz[A]||!sz[B])return -1;                //没有出现,肯定无解
        if(sz[A]>sz[B])swap(x,y),swap(A,B);         //交换后仍等价
        if(sz[B]<block)return merge_ans(A,B);       //都小于lim,暴力合并
        if(sz[A]<block)return min(ans[big[B]][A],merge_ans(A,B));//合并psz[x]和psz[y],再加上ans[][]
        return min(min(ans[big[A]][B],ans[big[B]][A]),merge_ans(A,B));//同上
    }
    inline void MAIN(){
        n=read(),m=read(),block=BB;
        for(res i=1;i<=n;i++)sz[a[i]=read()]++,ys[i]=i;
        for(res i=1;i<=n;i++)
            if(sz[i]>=block)bigbuild(i),v[i].resize(10);    //sz[x]>lim,重构
            else v[i].resize(sz[i]+10),sz[i]=0;
        for(res i=1;i<=n;i++)if(sz[a[i]]<block)v[a[i]][++sz[a[i]]]=i;
        while(m--){
            res opt=read(),x=read()^lastans,y=read()^lastans;
            if(opt==1)modify(x,y);
            else {
                res ret=query(x,y);
                if(ret==-1)puts("Chtholly"),lastans=0;
                else printf("%d\n",lastans=ret);
            }
        }
    }
}
int main() {
//    srand((unsigned)time(NULL));
//    freopen("graph.in","r",stdin);
//    freopen("graph.out","w",stdout);
    MAIN::MAIN();
    return 0;
}

Guess you like

Origin www.cnblogs.com/water-lift/p/10993782.html