• coci paint


    在sub2中,我们使用并查集维护每一个颜色相同连续段。
    在并查集的根部存储当前的颜色和连续段的左/右端点。
    每次尝试拓展一下。
    在sub3中,根据sub2的启发,我们也维护连通块使得相邻的连通块颜色不同。
    在修改时,如果我们成功把当前点修改成另一个颜色,则当前点的所有相邻点都会和这个点合并。
    维护一个点的所有相邻点可以启发式合并。
    注意到如果我们对一个连通块(x)的所有连向的连通块(y)进行合并,则(x->y)的边都没用了,可以被删除。
    这个删除操作保证了时间复杂度,使每条边只会被遍历1次。
    时间复杂度(RSlog_2{RS})
    48分代码(没调完)

    #include<bits/stdc++.h>
    using namespace std;
    #define N 200010
    int a,b,l[N],r[N],v[N],p[N],q,t[N],c[N],tx[4]={1,0,-1,0},ty[4]={0,-1,0,1},tp,vi[N];
    int fd(int x){
    	return !p[x]?x:p[x]=fd(p[x]); 
    }
    int id(int x,int y){
    	return (x-1)*b+y;
    }
    struct no{
    	int x,y;
    }st[N];
    queue<no>qu;
    unordered_map<int,int>ma[N];
    void mg(unordered_map<int,int> &x,unordered_map<int,int> &y){
    	if(x.size()<y.size())
    		swap(x,y);
    	for(unordered_map<int,int>::iterator it=x.begin();it!=x.end();it++){
    		int a=it->first,b=it->second;
    		x[a]+=b;
    	}
    	y.clear();
    }
    int main(){
    	//freopen("r.in","r",stdin);
    	scanf("%d%d",&a,&b);
    	if(a==1){
    		for(int i=1;i<=b;i++)
    			scanf("%d",&v[i]);
    		int j=1;
    		for(int i=1;i<=b;){
    			j=i;
    			while(j<b&&v[i]==v[j+1])
    				j++;
    			for(int k=i;k<=j;k++){
    				int xx=fd(i),yy=fd(k);
    				if(xx!=yy)
    					p[xx]=yy;
    			}
    			l[fd(i)]=i;
    			r[fd(i)]=j;
    			t[fd(i)]=v[i];
    			i=j+1;
    		}
    		scanf("%d",&q);
    		while(q--){
    			int x,y,c;
    			scanf("%d%d%d",&x,&y,&c);
    			t[fd(y)]=c;
    			while(1){
    				int g=fd(y),e=l[g],f=r[g],ok=0;
    				if(e>1&&t[fd(e-1)]==t[g]){
    					int yy=fd(e-1);
    					p[yy]=g;
    					l[g]=l[yy];
    					ok=1;
    				}
    				if(f<b&&t[fd(f+1)]==t[g]){
    					int yy=fd(f+1);
    					p[yy]=g;
    					r[g]=r[yy];
    					ok=1;
    				}
    				if(!ok)
    					break;
    			}
    		}
    		for(int i=1;i<=b;i++)
    			printf("%d ",t[fd(i)]);
    		return 0;
    	}
    	if(a*b<=10000){
    		for(int i=1;i<=a;i++)
    			for(int j=1;j<=b;j++)
    				scanf("%d",&c[id(i,j)]);
    		int q,ct=0;
    		scanf("%d",&q);
    		while(q--){
    			int x,y,z;
    			scanf("%d%d%d",&x,&y,&z);
    			qu.push((no){x,y});
    			while(!qu.empty()){
    				no x=qu.front();
    				qu.pop();
    				st[++tp]=x;
    				int e=x.x,f=x.y;
    				vi[id(e,f)]=1;
    				for(int i=0;i<4;i++){
    					int dx=e+tx[i],dy=f+ty[i];
    					if(dx<1||dx>a||dy<1||dy>b)
    						continue;
    					if(!vi[id(dx,dy)]&&c[id(dx,dy)]==c[id(e,f)]){
    						qu.push((no){dx,dy});
    						vi[id(dx,dy)]=1;
    					}
    				}
    			}
    			for(int i=1;i<=tp;i++){
    				c[id(st[i].x,st[i].y)]=z;
    				vi[id(st[i].x,st[i].y)]=0;
    			}
    			tp=0;
    		}
    		for(int i=1;i<=a;i++){
    			for(int j=1;j<=b;j++)
    				printf("%d ",c[id(i,j)]);
    			puts("");
    		}
    		return 0;
    	}
    	int ok=1;
    	for(int i=1;i<=a;i++)
    		for(int j=1;j<=b;j++){
    			scanf("%d",&c[id(i,j)]);
    			if(c[id(i,j)]!=1&&c[id(i,j)]!=0)
    				ok=0;
    		}
    	int q;
    	scanf("%d",&q);
    	for(int i=1;i<=q;i++){
    		scanf("%d%d%d",&l[i],&r[i],&v[i]);
    		if(v[i]!=1&&v[i]!=0)
    			ok=0;
    	}
    	if(ok){
    		for(int i=1;i<=a;i++)
    			for(int j=1;j<=b;j++)
    				for(int d=0;d<4;d++){
    					int dx=i+tx[d],dy=j+ty[d];
    					if(dx<1||dx>a||dy<1||dy>b)
    						continue;
    					if(c[id(dx,dy)]==c[id(i,j)]){
    						ma[id(i,j)][id(dx,dy)]=1;
    						ma[id(dx,dy)][id(i,j)]=1;
    					}
    					t[id(i,j)]=c[id(i,j)];
    				}
    		for(int i=1;i<=a;i++)
    			for(int j=1;j<=b;j++)
    				if(!vi[id(i,j)]){
    					qu.push((no){i,j});
    					while(!qu.empty()){
    						no x=qu.front();
    						qu.pop();
    						int e=x.x,f=x.y;
    						vi[id(e,f)]=1;
    						for(int t=0;t<4;t++){
    							int dx=e+tx[t],dy=f+ty[t];
    							if(dx<1||dx>a||dy<1||dy>b)
    								continue;
    							if(!vi[id(dx,dy)]&&c[id(dx,dy)]==c[id(e,f)]){
    								int xx=fd(id(e,f)),yy=fd(id(dx,dy));
    								ma[xx].erase(yy);
    								ma[yy].erase(xx);
    								mg(ma[xx],ma[yy]);
    								p[xx]=yy;
    								qu.push((no){dx,dy});
    								vi[id(dx,dy)]=1;
    							}
    						}
    					}
    				}
    		for(int i=1;i<=q;i++){
    			int xx=fd(id(l[i],r[i]));
    			t[xx]=v[i];
    			for(unordered_map<int,int>::iterator it=ma[xx].begin();it!=ma[xx].end();it++){
    				int a=it->first,b=it->second;
    				int yy=fd(a);
    				ma[xx].erase(yy);
    				ma[yy].erase(xx);
    				p[yy]=xx;
    				mg(ma[xx],ma[yy]);
    			}
    		}
    		for(int i=1;i<=a;i++){
    			for(int j=1;j<=b;j++)
    				printf("%d ",t[fd(id(i,j))]);
    			puts("");
    		}
    	}
    }
    

    48分代码的局限性:我们在合并的时候需要合并和当前点颜色相同的点。
    而在上一问的时候,我们不需要寻找当前点相邻的与当前点颜色相同的点。
    考虑按照大小分块。(其他的都不太行)
    如果一个连通块点数(<B),定义一个连通块为小的,否则定义为大的。
    考虑我们对一个小的连通块进行操作。由于它的边界点数肯定是(<B)的,所以连出的边条数肯定(<B)
    考虑对每个点维护出边的边集和它连出的点在下文中链表的编号。
    对于大点,还需维护每个点连出的相同颜色的点的链表和它邻接的大点编号。
    使用并查集在根部存储颜色。
    分四种情况:
    1.改的点是小点,改完后当前点的相邻点没有和当前点相同的点。
    遍历当前连通块的所有出边,如果是大点则更新大点的链表。
    2.改的点是小点,改完后当前点的相邻点有和当前点相同的点。
    遍历当前连通块的所有出边。
    把所有出边指向的点的相邻边集启发式合并。
    我们在删除的时候,只需要删除当前连通块点连向外面的出边。
    还要更新和当前点直接相连的,和当前点颜色不同的大点的链表。
    3.改的点是大点,改完后当前点的相邻点没有和当前点相同的点。
    遍历当前连通块的所有出边,如果是大点则更新大点的链表。
    总之此题思维难度不高但是细节较多。

  • 相关阅读:
    印度、日本、美国和国内的软件企业的“软件态度”
    限制文本框每行输入的字符数的C#代码
    之我见:没有像样的需求分析、设计就草草开始分块写代码
    Excel 加载项的怪现象
    Excel加载项Dotfuscator注意点
    设计者应增加对本机缓存特性的注意度
    我们的产品有技术含量吗?
    阳光行为,阳光心情
    Google在中国境内设服务器?速度快极了!
    标准重要不重要:标准目录查询网页竟有这样的语句:见证中国目前的软件开发水平
  • 原文地址:https://www.cnblogs.com/ctmlpfs/p/14137717.html
Copyright © 2020-2023  润新知