[SCOI2016] 萌萌哒

一、题目

点此看题

二、解法

首先拆限制,看似他给的是区间相等,其实是若干组单点相等。

那么把单点的限制用并查集连起来,我们只需要关系联通块个数即可。

问题转化为了每次给两个区间,要求区间对应位连边。线段树优化建图做不了,但是 \(st\) 表可以,设 \(fa[i][j]\) 表示以 \(i\) 为左端点,长度为 \(2^j\) 区间的并查集,我们把这个看成节点,但他的作用实际上是打标记

初始化 \(fa[i][..]=i\),打标记时直接把标记节点的并查集连起来,最后还需要下传一次,对于点 \((i,j)\),我们找到他在并查集上的父亲 \((x,j)\),然后把 \((i,j-1),(x,j-1)\)\((i+2^{j-1},j-1),(x+2^{j-1},j-1)\) 分别连起来即可。

三、总结

\(st\) 表对位优化建图可以当做一个小套路记下来,但是倍增是真的强,在区间问题中我们要主动使用它。

#include <cstdio>
const int M = 100005;
const int MOD = 1e9+7;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,ans,fa[M][20];
int find(int x,int y)
{
	if(fa[x][y]!=x) fa[x][y]=find(fa[x][y],y);
	return fa[x][y];
}
void merge(int x,int y,int k)
{
	int u=find(x,k),v=find(y,k);
	fa[u][k]=v;
}
signed main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++)
		for(int j=0;j<=19;j++)
			fa[i][j]=i;
	for(int i=1;i<=m;i++)
	{
		int l1=read(),r1=read(),l2=read(),r2=read();
		for(int j=19;j>=0;j--)
			if(l1+(1<<j)-1<=r1)
			{
				merge(l1,l2,j);
				l1+=(1<<j);l2+=(1<<j);
			}
	}
	for(int j=19;j>=1;j--)
		for(int i=1;i+(1<<j)-1<=n;i++)
		{
			int x=find(i,j);
			merge(i,x,j-1);
			merge(i+(1<<j-1),x+(1<<j-1),j-1);
		}
	for(int i=1;i<=n;i++)
		if(fa[i][0]==i)
			ans=!ans?9:(ans*10ll%MOD);
	printf("%d\n",ans);
}
posted @ 2021-07-15 22:21  C202044zxy  阅读(47)  评论(0编辑  收藏  举报