luogu P5397 [Ynoi2018]天降之物
Posted smyjr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu P5397 [Ynoi2018]天降之物相关的知识,希望对你有一定的参考价值。
下面令(n,q)同阶
先考虑暴力做法,询问是要对两个位置集合,选两个元素出来,求最小的差的绝对值.因为对于一个元素,一定选另一个集合中和他位置最近的前后两个元素最优,所以暴力是让集合为升序排列,再维护两个指针,一开始指向集合第一个元素,这同时维护之前扫过的元素中两个集合的最后一个元素,然后取出两个指针指向的较小的元素,用这个元素-另一个集合扫过的最后一个元素更新答案
这题显然没有什么传统数据结构可以维护,所以考虑根号分治,设置一个阈值(lim).先考虑没有修改操作
- 询问的两个集合大小(le lim),那么直接暴力,这部分复杂度为(O(n*lim))
- 有一个集合大小(>lim),因为这样的集合个数(le frac{n}{lim})个,所以在一开始先预处理出大小(>lim)的集合和其他集合的答案并且存下来,具体实现可以参考上述暴力做法,询问的时候直接查即可,这部分复杂度为(O(n*frac{n}{lim}))
在(lim=sqrt{n})时取到最优复杂度(O(nsqrt{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(nsqrt{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
",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
",ans);
}
}
return 0;
}
以上是关于luogu P5397 [Ynoi2018]天降之物的主要内容,如果未能解决你的问题,请参考以下文章
luogu P4688 [Ynoi2016]掉进兔子洞 bitset 莫队