Codeforces 1109F. Sasha and Algorithm of Silence's Sounds

Description

给出一个 n ∗ m n*m nm 的网格图,保证所有位置上的数形成一个 1 − n ∗ m 1 -n*m 1nm 的排列。

问有多少个值域区间 [ l , r ] [l,r] [l,r] 满足,在 [ l , r ] [l,r] [l,r] 中的数在网格图上的位置形成一棵树。

n ∗ m ≤ 200000 , n , m ≤ 1000 n*m\leq200000,n,m\leq1000 nm200000,n,m1000

题目链接:Codeforces 1109F. Sasha and Algorithm of Silence’s Sounds

Solution

  • 考虑到形成一棵树要同时满足两个条件:无环、连通

  • 维护两个指针 l , r l,r l,r ,表示当前做到的值域范围。

  • 每次将 r r r 右移一位,用 LCT 维护最小的 l l l 使得将 [ l , r ] [l,r] [l,r] 的点加入后没有成环,即森林(条件①)。

  • 那么我们要计算的就是这个区间内有多少个 l ′ l' l 使得 [ l ′ , r ] [l',r] [l,r] 满足条件。

  • 由于 连通块个数 = 点数 - 边数 ,那么我们维护一棵线段树记录每个位置的 “点数 - 边数” 。

  • 注意到这个值是非负的,于是线段树统计区间最小值及其个数,支持区间加减和区间查询即可。

  • 我们看看区间 [ l , r ] [l,r] [l,r] 内的最小值是不是 1 1 1 ,是就加上其个数就可以了(条件②)。

  • 时间复杂度 O ( n m   l o g   ( n m ) ) O(nm\ log\ (nm)) O(nm log (nm))

Code

#include
#include
#include
using namespace std;
typedef pair<int,int> PI;
const int N=2e5+5,M=1005;
const int way[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
struct node
{
	int x,y;
}a[N];
int n,m,up,top,qx,qy,qz;
long long ans;
int fa[N],s[N][2],st[N];
bool rev[N];
PI f[N<<2];
int b[M][M],c[N<<2];
inline int read()
{
	int X=0,w=0; char ch=0;
	while(!isdigit(ch)) w|=ch=='-',ch=getchar();
	while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
	return w?-X:X;
}
inline bool pd(int x)
{
	return x==s[fa[x]][1];
}
inline bool isroot(int x)
{
	return x^s[fa[x]][0] && x^s[fa[x]][1];
}
inline void rotate(int x)
{
	int y=fa[x],w=pd(x);
	if((fa[x]=fa[y]) && !isroot(y)) s[fa[y]][pd(y)]=x;
	if(s[y][w]=s[x][w^1]) fa[s[y][w]]=y;
	s[fa[y]=x][w^1]=y;
}
inline void reverse(int x)
{
	if(x) swap(s[x][0],s[x][1]),rev[x]^=1;
}
inline void down(int x)
{
	if(rev[x])
	{
		reverse(s[x][0]),reverse(s[x][1]);
		rev[x]=false;
	}
}
inline void splay(int x)
{
	for(int y=st[top=1]=x;!isroot(y);y=fa[y]) st[++top]=fa[y];
	while(top) down(st[top--]);
	for(int y;!isroot(x);rotate(x))
		if(!isroot(y=fa[x])) rotate(pd(x)==pd(y)?y:x);
}
inline void access(int x)
{
	for(int y=0;x;x=fa[y=x]) splay(x),s[x][1]=y;
}
inline void mkroot(int x)
{
	access(x),splay(x),reverse(x);
}
inline void link(int x,int y)
{
	mkroot(x),fa[x]=y;
}
inline void cut(int x,int y)
{
	mkroot(x),access(y),splay(y);
	s[y][0]=fa[x]=0;
}
inline bool connect(int x,int y)
{
	mkroot(x),access(y),splay(y);
	while(s[y][0]) y=s[y][0];
	return x==y;
}
inline void down1(int v)
{
	if(c[v])
	{
		f[v<<1].first+=c[v];
		f[v<<1|1].first+=c[v];
		c[v<<1]+=c[v];
		c[v<<1|1]+=c[v];
		c[v]=0;
	}
}
void make(int v,int l,int r)
{
	if(l==r)
	{
		f[v].second=1;
		return;
	}
	int mid=l+r>>1;
	make(v<<1,l,mid);
	make(v<<1|1,mid+1,r);
	f[v].second=f[v<<1].second+f[v<<1|1].second;
}
PI cmin(PI x,PI y)
{
	if(x.first<y.first) return x;
	if(x.first>y.first) return y;
	return make_pair(x.first,x.second+y.second);
}
void change(int v,int l,int r)
{
	if(qx<=l && r<=qy)
	{
		f[v].first+=qz;
		c[v]+=qz;
		return;
	}
	down1(v);
	int mid=l+r>>1;
	if(qx<=mid) change(v<<1,l,mid);
	if(qy>mid) change(v<<1|1,mid+1,r);
	f[v]=cmin(f[v<<1],f[v<<1|1]);
}
PI find(int v,int l,int r)
{
	if(qx<=l && r<=qy) return f[v];
	down1(v);
	int mid=l+r>>1;
	PI val(N,0);
	if(qx<=mid) val=find(v<<1,l,mid);
	if(qy>mid) val=cmin(val,find(v<<1|1,mid+1,r));
	return val;
}
inline bool judge(int l,int r)
{
	for(int i=0;i<3;i++)
	{
		int x=a[r].x+way[i][0],y=a[r].y+way[i][1];
		if(!x || x>n || !y || y>m) continue;
		if(b[x][y]<l || b[x][y]>r) continue;
		for(int j=i+1;j<4;j++)
		{
			int xx=a[r].x+way[j][0],yy=a[r].y+way[j][1];
			if(!xx || xx>n || !yy || yy>m) continue;
			if(b[xx][yy]<l || b[xx][yy]>r) continue;
			if(connect(b[x][y],b[xx][yy])) return true;
		}
	}
	return false;
}
inline void del(int l)
{
	for(int i=0;i<4;i++)
	{
		int x=a[l].x+way[i][0],y=a[l].y+way[i][1];
		if(!x || x>n || !y || y>m) continue;
		if(connect(b[x][y],l))
			cut(b[x][y],l);
	}
}
inline void add(int l,int r)
{
	for(int i=0;i<4;i++)
	{
		int x=a[r].x+way[i][0],y=a[r].y+way[i][1];
		if(!x || x>n || !y || y>m) continue;
		if(l<=b[x][y] && b[x][y]<r)
		{
			link(b[x][y],r);
			qx=1,qy=b[x][y],qz=-1;
			change(1,1,up);
		}
	}
	qx=l,qy=r,qz=1;
	change(1,1,up);
}
int main()
{
	n=read(),m=read(),up=n*m;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			b[i][j]=read();
			a[b[i][j]]=(node){i,j};
		}
	make(1,1,up);
	for(int i=1,j=1;i<=up;i++)
	{
		while(judge(j,i)) del(j++);
		add(j,i);
		qx=j,qy=i;
		PI sum=find(1,1,up);
		ans+=sum.first==1?sum.second:0;
	}
	printf("%I64d",ans);
	return 0;
}

你可能感兴趣的:(LCT,线段树)