Ynoi2018 天降之物

Link
我们知道在没有修改的时候有一个很简单的根号分治算法,现在考虑将其扩展至带修。
\(cnt_x\)\(x\)的出现次数,\(pos_x\)\(x\)的出现位置,\(dis_x\)\(x\)到其它数的最短距离。
\(B\)为分块大小,\(S=\{x|cnt_x>B\}\)

这里先给出整体的大致思路:
保证任意时刻\(|pos_x|\le B\),这样我们可以用\(O(B)\)的时间进行零散的合并/查询。
如果\(|pos_x|>B\)那么我们处理\(dis_x\),很显然处理\(dis\)的总次数是\(O(\frac nB)\)的。

然后我们给出具体做法:
最开始先求出\(pos,cnt\),对于\(x\in S\),清空\(pos_x\)并求出\(dis_x\)
考虑如何处理修改:
\(1.\)归并\(pos_x,pos_y\)并清空\(pos_x\)
\(2.\)如果合并之后\(|pos_y|>S\),那么清空\(pos_y\)并更新\(dis_y\)
\(3.\)如果\(x\in S\),那么用\(dis_x\)更新\(dis_y\)并清空\(dis_x\)
\(4.\)对所有\(w\in S\),用\(dis_{w,x}\)更新\(dis_{w,y}\)
考虑如何回答询问:
\(1.\)先特判\(ans=0\)的情况。
\(2.\)利用\(dis_{x,y},dis_{y,x}\)更新\(ans\)
\(3.\)遍历\(pos_x,pos_y\)更新\(ans\)

不难发现这样做可以完整地维护与统计\(pos,dis\)的信息。
总的时间复杂度是\(O(nB+\frac{n^2}B)\),当\(B=\sqrt n\)达到最优\(O(n^{1.5})\)
注意到空间复杂度是\(O(\frac{n^2}B)\)的,因此可以适当调大\(B\)来卡空间。

#include<cmath>
#include<cstdio>
#include<cctype>
#include<vector>
#include<algorithm>
#include<unordered_set>
namespace IO
{
    char ibuf[(1<<21)+1],*iS,*iT;
    char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
    int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
}
using IO::read;
const int N=100001,B=3,inf=1000000000;
int n,q,cnt=100000,a[N],fa[N*3],num[N*3],id[N*3];
std::vector<int>pos[N],dis[N],t;
std::unordered_set<int>res;
int find(int x){return fa[x]==x? x:fa[x]=find(fa[x]);}
int get(int x){return num[find(a[x])];}
void min(int&a,int b){a=a<b? a:b;}
void init(int i)
{
    dis[i].resize(100001,inf);
    for(int j=1,p=0;j<=n;++j)
    {
	while(p+1<(int)pos[i].size()&&j>pos[i][p]&&j-pos[i][p]>pos[i][p+1]-j) ++p;
	min(dis[i][get(j)],abs(j-pos[i][p]));
    }
    pos[i].clear(),res.insert(i);
}
int main()
{
    freopen("1.in","r",stdin);freopen("1.out","w",stdout);
    n=read(),q=read();
    for(int i=1;i<=n;++i) pos[a[i]=read()].push_back(i);
    for(int i=1;i<=300000;++i) fa[i]=i;
    for(int i=1;i<=100000;++i) num[i]=id[i]=i;
    for(int i=0;i<=100000;++i) if(pos[i].size()>=B) init(i);
    for(int opt,x,y,ans=0;q;--q)
    {
	opt=read(),x=read()^ans,y=read()^ans;
	if(opt==1)
	{
	    if(x==y) continue;
	    num[++cnt]=y,fa[find(id[x])]=fa[find(id[y])]=cnt,id[y]=cnt,id[x]=++cnt,num[id[x]]=x;
	    t.clear(),t.resize(pos[x].size()+pos[y].size()),std::merge(pos[x].begin(),pos[x].end(),pos[y].begin(),pos[y].end(),t.begin());
	    pos[x].clear(),pos[y]=t,res.erase(x),res.erase(y);
	    if(dis[x].size()||dis[y].size())
	    {
		if(dis[y].empty()) dis[y].swap(dis[x]);
		else if(dis[x].size()) {for(int i=0;i<=100000;++i)min(dis[y][i],dis[x][i]);dis[x].clear();}
		res.insert(y);
	    }
	    if(pos[y].size()>=B) init(y);
	    for(int i:res) min(dis[i][y],dis[i][x]),dis[i][x]=inf;
	}
	else
	{
	    ans=inf;
	    if(x==y&&(pos[x].size()||dis[x].size())) ans=0;
	    if(dis[x].size()) min(ans,dis[x][y]);
	    if(dis[y].size()) min(ans,dis[y][x]);
	    for(int i=0,j=0;i<(int)pos[x].size()||j<(int)pos[y].size();)
		if(i<(int)pos[x].size()&&(j==(int)pos[y].size()||pos[x][i]<pos[y][j])) j? min(ans,pos[x][i]-pos[y][j-1]):void(),++i;
		else i? min(ans,pos[y][j]-pos[x][i-1]):void(),++j;
	    ans==inf? puts("Ikaros"),ans=0:printf("%d\n",ans);
	}
    }
}
posted @ 2020-02-15 14:58  Shiina_Mashiro  阅读(276)  评论(0编辑  收藏  举报