[Codeforces] 1109F. Sasha and Algorithm of Silence's Sounds LCT+线段树

Solution

假如要求的不是一棵树而是一个森林那就很好做,直接用一个双指针+LCT就可以对每个右端点维护出没有环的左端点。那么树这个限制怎么解决呢?树也就是连通块只有一个,而连通块数=点数-边数,用一个线段树维护这个东西就行了。

Code

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=200010;
const int inf=2147483647;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
int n,m,a[1010][1010];LL ans=0;
int son[Maxn][2],fa[Maxn];bool rev[Maxn];
bool is(int x){return(son[fa[x]][0]!=x&&son[fa[x]][1]!=x);}
void Rev(int x)
{
	rev[x]^=true;
	swap(son[x][0],son[x][1]);
}
void down(int x)
{
	if(rev[x])
	{
		int lc=son[x][0],rc=son[x][1];
		if(lc)Rev(lc);
		if(rc)Rev(rc);
		rev[x]=false;
	}
}
void rotate(int x)
{
	int y=fa[x],z=fa[y],w=(son[y][0]==x);
	son[y][w^1]=son[x][w];if(son[x][w])fa[son[x][w]]=y;
	if(!is(y))son[z][son[z][1]==y]=x;fa[x]=z;
	son[x][w]=y;fa[y]=x;
}
int sta[Maxn],top;
void update(int x)
{
	top=0;
	while(1)
	{
		sta[++top]=x;
		if(is(x))break;
		x=fa[x];
	}
	while(top)down(sta[top--]);
}
void splay(int x)
{
	update(x);
	while(!is(x))
	{
		int y=fa[x],z=fa[y];
		if(!is(y))rotate((son[z][1]==y)==(son[y][1]==x)?y:x);
		rotate(x);
	}
}
void access(int x)
{
	int last=0;
	while(x)
	{
		splay(x);
		son[x][1]=last;
		last=x;x=fa[x];
	}
}
void make_root(int x){access(x),splay(x),Rev(x);}
void link(int x,int y){/*printf("link %d %d\n",x,y);*/make_root(x),fa[x]=y;}
void cut(int x,int y){/*printf("cut %d %d\n",x,y);*/make_root(x),access(y),splay(y);son[y][0]=fa[x]=0;}
int find_root(int x){access(x),splay(x);while(son[x][0])x=son[x][0];return x;}
struct P{int mn,c;P(int _mn=0,int _c=0){mn=_mn,c=_c;}};
struct Seg{int l,r,lc,rc,tag;P t;}tr[Maxn<<1];
int tot=0;
void Add(int x,int v){tr[x].t.mn+=v;tr[x].tag+=v;}
P merge(P lc,P rc)
{
	P re;
	if(lc.mn==rc.mn)re.c=lc.c+rc.c;
	else re.c=((lc.mn<rc.mn)?lc.c:rc.c);
	re.mn=min(lc.mn,rc.mn);
	return re;
}
void Down(int x)
{
	int lc=tr[x].lc,rc=tr[x].rc,t=tr[x].tag;
	if(t)
	{
		tr[x].tag=0;
		Add(lc,t),Add(rc,t);
	}
}
void build(int l,int r)
{
	int x=++tot;
	tr[x].l=l;tr[x].r=r;tr[x].tag=0;
	if(l==r){tr[x].t=P(0,1);return;}
	int mid=l+r>>1;
	tr[x].lc=tot+1,build(l,mid);
	tr[x].rc=tot+1,build(mid+1,r);
	tr[x].t=merge(tr[tr[x].lc].t,tr[tr[x].rc].t);
}
void add(int x,int l,int r,int v)
{
	if(l>r)return;
	if(tr[x].l==l&&tr[x].r==r){Add(x,v);return;}
	int mid=tr[x].l+tr[x].r>>1,lc=tr[x].lc,rc=tr[x].rc;
	Down(x);
	if(r<=mid)add(lc,l,r,v);
	else if(l>mid)add(rc,l,r,v);
	else add(lc,l,mid,v),add(rc,mid+1,r,v);
	tr[x].t=merge(tr[tr[x].lc].t,tr[tr[x].rc].t);
}
P query(int x,int l,int r)
{
	if(tr[x].l==l&&tr[x].r==r)return tr[x].t;
	int mid=tr[x].l+tr[x].r>>1,lc=tr[x].lc,rc=tr[x].rc;
	Down(x);
	if(r<=mid)return query(lc,l,r);
	if(l>mid)return query(rc,l,r);
	return merge(query(lc,l,mid),query(rc,mid+1,r));
}
int px[Maxn],py[Maxn];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
bool in(int x,int y){return(x>0&&y>0&&x<=n&&y<=m);}
int l,r,tmp[5],lt;
bool check(int x,int y)
{
	lt=0;
	for(int i=0;i<4;i++)
	{
		int nx=x+dx[i],ny=y+dy[i];
		if(!in(nx,ny))continue;
		if(l<=a[nx][ny]&&a[nx][ny]<=r)
		{
			int t=find_root(a[nx][ny]);
			for(int j=1;j<=lt;j++)
			if(tmp[j]==t)
			{
				return false;
			}
			tmp[++lt]=t;
		}
	}
	return true;
}
int main()
{
	n=read(),m=read();
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		int x=read();
		a[i][j]=x;
		px[x]=i,py[x]=j;
	}
	build(1,n*m);add(1,1,1,1);
	l=1;ans++;
	for(r=2;r<=n*m;r++)
	{
		while(!check(px[r],py[r]))
		{
			for(int i=0;i<4;i++)
			{
				int nx=px[l]+dx[i],ny=py[l]+dy[i];
				if(in(nx,ny)&&a[nx][ny]>l&&a[nx][ny]<r)
				{
					cut(l,a[nx][ny]);
				}
			}
			l++;
		}
		add(1,l,r,1);
		for(int i=0;i<4;i++)
		{
			int nx=px[r]+dx[i],ny=py[r]+dy[i];
			if(!in(nx,ny))continue;
			if(l<=a[nx][ny]&&a[nx][ny]<=r)
			{
				link(a[nx][ny],r);
				add(1,l,a[nx][ny],-1);
			}
		}
		P t=query(1,l,r);
		if(t.mn==1)ans+=(LL)t.c;
	}
	printf("%lld",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值