zoukankan      html  css  js  c++  java
  • 题解 P5397 【[Ynoi2018]天降之物】

    lxl的博客讲的挺清楚了。

    如果不管时间复杂度,能想到的最暴力的做法是什么?

    用vector对于每个值维护出现的位置(从小到大),查询的时候暴力归并,修改也是暴力归并。

    于是就可以获得60分的成绩(随机数据下跑的挺快的qwq),然后就可以看别人的AC代码了

    再看看这题。暴力时间复杂度错误的原因是那个位置的集合太大了(按lxl的叫法,叫做附属集合)。那就把那个集合弄小一点嘛,那多大可以接受呢?这是Ynoi啊,当然是根号

    于是就想到了 根号分治

    (siz[x]) 表示 (x) 在集合中出现的次数,(siz[x]>lim) 的称作大点((lim) 是阈值), 否则称为小点。

    但是怎么维持附属集合的大小在 (lim) 以内呢?考虑在附属集合大小超过 (lim) 的时候直接 (O(n)) 暴力处理这个大点(显然是个大点,小点的附属集合不可能超过 (lim))到所有值的答案,设为 (ans[x][y])(x) 是大点, (y) 是每个值,然后清空 (y) 附属集合,于是就保证了所有附属集合大小不超过 (lim) 。后文把这个过程称作暴力重构。

    显然 ,(ans) 的答案并不包括附属集合内的答案。

    接下去的讨论默认 (siz[x]<siz[y]) ,因为有种技巧可以使得保证 (siz[x]<siz[y]) ,讨论结束了会讲。

    对于修改,把 (x) 变成 (y)

    • 如果 (x,y) 均为小点,由于出现次数小于 (lim) ,直接暴力把 (x) 归并进 (y) 的附属集合,如果大于 (lim) 就把 (y) 设成大点,暴力重构 (y) ,由于大点显然最多 (dfrac{n}{lim}) 个,这个过程不会超过 (dfrac{n}{lim}) 次。

    • (x) 为小点, (y) 为大点

      直接把 (x) 并入 (y) 的附属集合即可,(y) 的附属集合大小如果超过 (lim) 则暴力重构(y)

      显然大点的个数不会超过 (dfrac{n}{lim}) ,又发现附属集合元素数量总和不超过 (n) ,所以暴力重构次数不会超过 (dfrac{n}{lim})

    • (x,y) 均为大点,显然可以暴力重构 (y) ,因为这个过程使得 (siz[y]) 至少增大 (lim) ,不会超过 (dfrac{n}{lim}) 次。

    综上,修改的复杂度被控制在了 (O(dfrac{nm}{lim}))

    对于查询 (x,y) 的最近距离:

    • 如果 (x,y) 均为小点,暴力归并 (x,y) 的附属集合。时间复杂度 (O(lim))
    • 如果 (x) 为小点, (y) 为大点,暴力归并 (x,y) 的附属集合,再与 (ans[y][x])(min) 。时间复杂度 (O(lim))
    • 如果 (x,y) 均为大点,暴力归并 (x,y) 的附属集合,再与 (ans[x][y],ans[y][x])(min) ,因为我们并不知道 (x,y) 哪个靠后。被重构,所以 (ans[x][y],ans[y][x]) 必须都要取 (min) 。 时间复杂度 (O(lim))

    综上,查询的复杂度是 (O(mcdot lim))

    如何保证 (siz[x]<siz[y]) 呢?我们加个数组 (F[i]) 表示 (i) 这个值实际是多少,初始化 (F[i]=i)

    如果要交换 (x,y) 那么 (F[x]=y,F[y]=x) 即可 。

    (lim)(sqrt n) 即可,实测 (lim=500) 更快,不过这题并不卡常。

    注意事项

    • 时刻记着 (ans) 是不包括附属集合的答案,不然代码会出一堆bug。
    • 暴力重构的时候要扫 (2) 遍,从前到后再从后到前。
    • 暴力重构完记得清空附属集合。
    • 出题人好像没卡,强制在线解密以后以及原来的数组都没有 (0) 出现,但是建议不要漏掉 (0) 的情况。
    • 最坑的一点了:vector 的 clear 并不释放空间

    所以清空vector应该这么写:

    void fyyAKCTS() {
        /*your code*/
        vector<int>fyyAKIOI;
    	v[x].swap(fyyAKIOI);
    }
    

    因为 (fyyAKIOI) 是个局部变量,到了 (fyyAKCTS) 函数的右大括号会被释放空间,而此时由于 (fyyAKIOI) 原本空的,(operatorname{swap}) 以后 (v[x]) 就是空的了,于是成功清空了vector并释放了空间 还因为尛了fyy加了rp

    code:

    const int N=100009;
    const int M=320;
    const int inf=0x3f3f3f3f;
    int n,m,a[N],lastans;
    int lim,siz[N],ans[M][N],id[N],F[N],tot;
    vector<int>v[N];
    void build(int x,int Id=0) {
    	id[x]=Id?Id:++tot;
    	int t=id[x];
    	memset(ans[t],0x3f,sizeof(ans[t]));
    	for(int i=1,j=inf;i<=n;++i)
    		if(a[i]==x)j=0;
    		else ans[t][a[i]]=min(ans[t][a[i]],++j);
    	for(int i=n,j=inf;i;--i)
    		if(a[i]==x)j=0;
    		else ans[t][a[i]]=min(ans[t][a[i]],++j);
    	vector<int>fyyAKIOI;
    	ans[t][x]=0,v[x].swap(fyyAKIOI);
    }
    void merge(int x,int y) {
    	int i=0,j=0,sx=v[x].size(),sy=v[y].size();
    	vector<int>res;
    	while(i<sx&&j<sy)res.push_back(v[x][i]<v[y][j]?v[x][i++]:v[y][j++]);
    	while(i<sx)res.push_back(v[x][i++]);
    	while(j<sy)res.push_back(v[y][j++]);
    	v[y]=res;
    }
    int merge_(int x,int y) {
    	int i=0,j=0,sx=v[x].size(),sy=v[y].size(),res=inf;
    	if(!sx||!sy)return inf;
    	while(i<sx&&j<sy)res=min(v[x][i]<v[y][j]?v[y][j]-v[x][i++]:v[x][i]-v[y][j++],res);
    	if(i<sx)res=min(res,abs(v[x][i]-v[y][sy-1]));
    	if(j<sy)res=min(res,abs(v[x][sx-1]-v[y][j]));
    	return res;
    }
    int query(int x,int y) {
    	x=F[x],y=F[y];
    	if(!siz[x]||!siz[y])return -1;
    	if(x==y)return 0;
    	if(siz[x]>siz[y])x^=y^=x^=y;
    	if(siz[y]<=lim)return merge_(x,y);
    	else if(siz[x]<=lim)return min(ans[id[y]][x],merge_(x,y));
    	else return min(merge_(x,y),min(ans[id[x]][y],ans[id[y]][x]));
    }
    void change(int x,int y) {
    	int x_=F[x],y_=F[y];
    	if(!siz[x_]||x_==y_)return;
    	if(siz[x_]>siz[y_])F[y]=x_,F[x]=n+1,x_^=y_^=x_^=y_;
    	else F[x]=n+1;
    	if(x_>n||y_>n)return;
    	x=x_,y=y_;
    	if(siz[y]<=lim) {
    		if(siz[x]+siz[y]<=lim) {
    			for(int i:v[x])a[i]=y;
    			for(int i=1;i<=tot;++i)ans[i][y]=min(ans[i][y],ans[i][x]);
    			merge(x,y);
    		} else {
    			for(int i=1;i<=n;++i)if(a[i]==x)a[i]=y;
    			build(y);
    		}
    	} else if(siz[x]<=lim) {
    		if(siz[x]+v[y].size()<=lim) {
    			for(int i:v[x])a[i]=y;
    			for(int i=1;i<=tot;++i)ans[i][y]=min(ans[i][y],ans[i][x]);
    			merge(x,y);
    		} else {
    			for(int i=1;i<=n;++i)if(a[i]==x)a[i]=y;
    			build(y,id[y]);
    		}
    	} else {
    		for(int i=1;i<=n;++i)if(a[i]==x)a[i]=y;
    		build(y,id[y]);
    	}
    	vector<int>fyyAKIOI;
    	siz[y]+=siz[x],siz[x]=0,v[x].swap(fyyAKIOI);
    }
    signed main() {
    	n=rd(),m=rd(),lim=sqrt(n);
    	for(int i=1;i<=n;++i)++siz[a[i]=rd()],v[a[i]].push_back(i),F[i]=i;
    	for(int i=0;i<=n;++i)if(siz[i]>lim)build(i);
    	while(m--) {
    		 int opt=rd(),x=rd()^lastans,y=rd()^lastans;
    		if(opt==1)change(x,y);
    		else {
    			lastans=query(x,y);
    			if(~lastans)printf("%d
    ",lastans);
    			else lastans=0,puts("Ikaros");
    		}
    	}
    	return 0;
    }
    

    由于用了fyyAKIOI还AC了,说明fyy真的AKIOI了

    路漫漫其修远兮,吾将上下而求索
  • 相关阅读:
    Python中的try...except...finally
    JavaScript 实现双向队列并用此来测试一个单词是否为回文
    js 触发 change 事件
    MySQL8 重置改root密码及开放远程访问
    根据数组下标在MongoDB中修改数组元素
    pymongo CursorNotFound错误
    mongodb根据子项中的指标查找最小或最大值
    正则文本过滤时的一些注意事项
    github page更新后不生效
    Xpath同时选取不同属性的元素
  • 原文地址:https://www.cnblogs.com/zzctommy/p/13565373.html
Copyright © 2011-2022 走看看