luogu P5397 [Ynoi2018]天降之物

luogu

下面令\(n,q\)同阶

先考虑暴力做法,询问是要对两个位置集合,选两个元素出来,求最小的差的绝对值.因为对于一个元素,一定选另一个集合中和他位置最近的前后两个元素最优,所以暴力是让集合为升序排列,再维护两个指针,一开始指向集合第一个元素,这同时维护之前扫过的元素中两个集合的最后一个元素,然后取出两个指针指向的较小的元素,用这个元素-另一个集合扫过的最后一个元素更新答案

这题显然没有什么传统数据结构可以维护,所以考虑根号分治,设置一个阈值\(lim\).先考虑没有修改操作

  • 询问的两个集合大小\(\le lim\),那么直接暴力,这部分复杂度为\(O(n*lim)\)
  • 有一个集合大小\(>lim\),因为这样的集合个数\(\le \frac{n}{lim}\)个,所以在一开始先预处理出大小\(>lim\)的集合和其他集合的答案并且存下来,具体实现可以参考上述暴力做法,询问的时候直接查即可,这部分复杂度为\(O(n*\frac{n}{lim})\)

\(lim=\sqrt{n}\)时取到最优复杂度\(O(n\sqrt{n})\)

然后是修改操作,修改操作也就是把两个集合合并,如果合并的两个集合大小都\(> lim\)或都\(\le lim\),复杂度是可以做到\(O(n(lim+\frac{n}{lim}))\)的.现在的问题是一个大小\(> lim\)和大小\(\le lim\)的集合合并.我们考虑把小的集合并到大集合上面去,这里给大集合附加一个集合存合并到大集合上的小集合元素,每次把小集合合并上去,然后如果附加集合大小\(>\sqrt{n}\)就把它和大集合合并,并且重新预处理大集合的答案,复杂度\(O(n*\frac{n}{lim})\);询问的时候就先询问大集合预处理的答案,然后对于两个集合的附加集合跑暴力,复杂度\(O(n*lim)\).这里同样在\(lim=\sqrt{n}\)时取到最优复杂度\(O(n\sqrt{n})\)

#include<bits/stdc++.h>
#define LL long long

using namespace std;
const int N=1e5+10,inf=1145141;
int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+(ch^48);ch=getchar();}
    return x*w; 
}
int n,m=100001,lm=333,q,ans,sz[N],co[N],pr[N],a[N],ff[N];
int findf(int x){return ff[x]==x?x:ff[x]=findf(ff[x]);}
vector<int> st[N],sb[N],an[N],z1,z2;
vector<int>::iterator it;
int bg[N],tb;
void bui(int x)
{
    for(int i=0;i<=m;++i) an[x][i]=inf;
    an[x][x]=0;
    for(int i=1,ls=-inf;i<=n;++i)
    {
        if((a[i]=findf(a[i]))==x) ls=i;
        else an[x][a[i]]=min(an[x][a[i]],i-ls);
    }
    for(int i=n,ls=inf+inf;i;--i)
    {
        if((a[i]=findf(a[i]))==x) ls=i;
        else an[x][a[i]]=min(an[x][a[i]],ls-i);
    }
}
vector<int> merg(vector<int> a,vector<int> b)
{
    int nn=a.size(),mm=b.size();
    vector<int> an;
    an.resize(nn+mm);
    int pa=0,pb=0,pc=0;
    while(pa<nn&&pb<mm)
    {
        if(a[pa]<b[pb]) an[pc++]=a[pa],++pa;
        else an[pc++]=b[pb],++pb;
    }
    while(pa<nn) an[pc++]=a[pa],++pa;
    while(pb<mm) an[pc++]=b[pb],++pb;
    return an;
}
int cn=0;

int main()
{
    n=rd(),q=rd();
    for(int i=1;i<=n;++i)
    {
        a[i]=rd()+1,ff[a[i]]=pr[a[i]]=a[i];
        st[a[i]].push_back(i);
    }
    for(int i=1;i<=m;++i)
    {
        sz[i]=st[i].size();
        if(sz[i]>=lm) an[i].resize(m+1),bg[++tb]=i,bui(i);
    }
    while(q--)
    {
        int op=rd(),x=(rd()^ans)+1,y=(rd()^ans)+1;
        if(op==1)
        {
            if(x==y) continue;
            int lx=x,ly=y;
            x=pr[lx],y=pr[ly],pr[lx]=pr[ly]=0;
            if(!x||!y){pr[ly]=x+y;continue;}
            int yy=ly;
            if(sz[x]+(int)sb[x].size()<sz[y]+(int)sb[y].size())
            swap(x,y),swap(lx,ly);
            ff[y]=x,pr[yy]=x;
            int lt=tb;tb=0;
            for(int i=1;i<=lt;++i)
            {
                if(bg[i]==y) continue;
                bg[++tb]=bg[i];
                an[bg[tb]][x]=min(an[bg[tb]][x],an[bg[tb]][y]),an[bg[tb]][y]=inf;
            }
            if((int)sb[x].size()+sz[y]+(int)sb[y].size()>=lm)
            {
                if(sz[x]<lm) an[x].resize(m+1),bg[++tb]=x;
                sz[x]+=(int)sb[x].size()+sz[y]+(int)sb[y].size();
                sz[y]=0,sb[x].clear()/*,st[x].clear(),sb[y].clear(),st[y].clear()*/;
                bui(x);
            }
            else
            {
                sb[x]=merg(sb[x],merg(st[y],sb[y]));
                sz[y]=0/*,st[y].clear(),sb[y].clear()*/;
            }
        }
        else
        {
            ++cn;
            x=pr[x],y=pr[y];
            if(!x||!y){puts("Ikaros"),ans=0;continue;}
            if(x==y){printf("%d\n",ans=0);continue;}
            ans=inf;
            z1=sb[x],z2=sb[y];
            if(sz[x]>=lm) ans=min(ans,an[x][y]);
            else z1=merg(z1,st[x]);
            if(sz[y]>=lm) ans=min(ans,an[y][x]);
            else z2=merg(z2,st[y]);
            int nn=z1.size(),mm=z2.size();
            for(int i=0,j=0,l1=-inf,l2=-inf;i<nn||j<mm;)
            {
                if(i<nn&&(j>=mm||z1[i]<z2[j]))
                {
                    ans=min(ans,z1[i]-l2),l1=z1[i];
                    ++i;
                }
                else
                {
                    ans=min(ans,z2[j]-l1),l2=z2[j];
                    ++j;
                }
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/smyjr/p/12304887.html
今日推荐