[CCO 2019]Sirtet - parallel heap + lazy mark acceleration simulation

Keywords: Algorithm data structure Simulation

[CCO 2019]Sirtet

preface

I don't understand. This problem is completely done according to the idea of simulation. Why does someone say that its essence is Dijkstra with differential constraints? The essence of Dijkstra is to do simulation, right?

Problem solution

First, consider how to simulate violence. The normal idea should be to let all rigid bodies that do not contact the ground move downward at the same time until a rigid body contacts the ground, then treat the rigid body that contacts the ground as the ground, and then let the other rigid bodies continue to fall. The complexity of violence simulation is O ( n 2 m ) O(n^2m) O(n2m).

Think about how to speed up the process. The connected rigid bodies are reduced to a point by using the union search set, and then the nearest # below each #. We connect each # corresponding connected block to the nearest # corresponding connected block below (directed edge). In particular, if the ground is below, we connect the edge to the ground. The length of the edge is the distance between two # or # to the ground. The number of edges is obviously the largest n m nm nm order of magnitude.

It may be useful to record all the edges connected to each connected block and all the edges connected to the ground first. We analyze the process of violent simulation. Each time we fall, the length of the edge between the connected block and the connected block will not change, but the length of each edge connected to the ground will be reduced by 1. Until the length of one of the edges is 0, the connected block corresponding to the starting point of the edge will contact the ground. The first edge with a length of 0 is actually the edge with the shortest length connected to the ground, so we can first put all the edges connected to the ground into a pile, then take out the shortest edge and add the corresponding connected block to the "ground", which can accelerate this process.

In this process, the length of other edges connected to the ground will subtract the length of the shortest edge. At the same time, the edge connected to the connected block counted as the ground will become a new batch of edges connected to the ground. If we want to maintain both of them quickly at the same time, we just need to type a merge heap with lazy flag. Each block maintains an edge set with a coalescer. After marking the original edge connected to the ground, merge the coalescer on the block. When we record the falling distance of each connected block while simulating, we can move each # block of the original graph to the correct position.

Each fetch and merge must reduce one edge, so the total complexity is the most O ( n m log ⁡ n m ) O(nm\log nm) O(nmlognm). This complexity does not reach the upper bound, and even with the lazy flag, the combinable heap constant is still very small.

code

#include<cstdio>//JZM yyds!!
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<random>
#define ll long long
#define uns unsigned
#define MOD
#define MAXN 1000005
#define INF 1e18
#define IF it->first
#define IS it->second
using namespace std;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0)f^=(s=='-'),s=getchar();
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
	return f?x:-x;
}
inline void print(int x){
	if(x<0)putchar('-'),print(-x);
	else{
		if(x/10>0)print(x/10);
		putchar(x%10+'0');
	}
}
inline ll lowbit(ll x){return x&-x;}

struct itn{
	int sn[2],a,d,lz;itn(){}
	itn(int A,int D){
		a=A,d=D,sn[0]=sn[1]=0,lz=0;
	}
}t[MAXN];
int IN,rt[MAXN],root;
inline bool cmp(int a,int b){return a>b;}
inline void cover(int x,int d){
	if(!x||!d)return;
	t[x].lz+=d,t[x].a-=d;
}
inline void pushd(int x){
	if(!x||!t[x].lz)return;
	cover(t[x].sn[0],t[x].lz);
	cover(t[x].sn[1],t[x].lz);
	t[x].lz=0;
}
mt19937 Rand;
inline int mergh(int x,int y){
	if(!x||!y)return x^y;
	pushd(x),pushd(y);
	if(cmp(t[x].a,t[y].a))swap(x,y);
	bool o=Rand()%2;
	if(!t[x].sn[o^1])o^=1;
	t[x].sn[o]=mergh(t[x].sn[o],y);
	return x;
}

char s[MAXN],as[MAXN];
int n,m,fa[MAXN],ds[MAXN],drp[MAXN];
bool dpd[MAXN];
inline int finds(int x){
	return fa[x]==x?x:(fa[x]=finds(fa[x]));
}
inline void unions(int x,int y){
	int u=finds(x),v=finds(y);
	if(u^v)fa[v]=u;
}
signed main()
{
	Rand.seed(*new(int));
	n=read(),m=read();
	for(int i=0;i<n*m;i++)fa[i]=i;
	for(int i=0;i<n;i++)scanf("%s",s+i*m);
	for(int i=1;i<n;i++)
		for(int j=0;j<m;j++)
			if(s[i*m+j]=='#'){
				if(s[(i-1)*m+j]=='#')
					unions((i-1)*m+j,i*m+j);
			}
	for(int i=0;i<n;i++)
		for(int j=1;j<m;j++)
			if(s[i*m+j]=='#'){
				if(s[i*m+j-1]=='#')
					unions(i*m+j-1,i*m+j);
			}
	for(int i=n-2;~i;i--)
		for(int j=0;j<m;j++){
			if(s[(i+1)*m+j]=='#')ds[i*m+j]=0;
			else ds[i*m+j]=ds[(i+1)*m+j]+1;
		}
	for(int i=n-1;~i;i--)
		for(int j=0;j<m;j++){
			if(s[i*m+j]=='.')continue;
			int u=i*m+j,vi=i+ds[u]+1;
			if(vi>=n){
				t[++IN]=itn(ds[u],finds(u));
				root=mergh(root,IN);
			}else{
				int v=finds(vi*m+j);
				t[++IN]=itn(ds[u],finds(u));
				rt[v]=mergh(rt[v],IN);
			}
		}
	ll ad=0;
	while(root){
		int u=root;pushd(root);
		root=mergh(t[root].sn[0],t[root].sn[1]);
		if(dpd[t[u].d])continue;
		ad+=t[u].a,dpd[t[u].d]=1,drp[t[u].d]=ad;
		cover(root,t[u].a);
		root=mergh(root,rt[t[u].d]);
	}
	for(int i=n*m-1;~i;i--)as[i]='.';
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			if(s[i*m+j]=='#'){
				int u=finds(i*m+j);
				as[(i+drp[u])*m+j]='#';
			}
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++)putchar(as[i*m+j]);
		putchar('\n');
	}
	return 0;
}

Posted by argoSquirrel on Fri, 15 Oct 2021 17:24:27 -0700