NOIP模拟赛 CQBZ 3501. survive【并查集+倍增】

题面:

在这里插入图片描述
n , m ≤ 1 0 5 n,m\le10^5 n,m105

题目分析:

容易想到的暴力做法是把每次的区间要求[l,r]拆分成r-l+1个单点要求,并查集维护单点要求,最后扫一遍查询。
复杂度是 O ( n m α ) O(nm\alpha) O(nmα)的,但是由于出题人太过良心,这样你就能得到80分。

把要求每次拆分很显然要T,我们考虑优化每次的要求,其时间就浪费在重复要求已经相等的点对上。
怎么办呢,其实只要再稍微一想就能想到ST表维护了。

把一个整段区间拆成两段2的幂, [ l 1 , r 1 ] [l_1,r_1] [l1,r1] [ l 2 , r 2 ] [l_2,r_2] [l2,r2]相等就相当于 [ l 1 , l 1 + 2 k − 1 ] [l_1,l_1+2^k-1] [l1,l1+2k1] [ l 2 , l 2 + 2 k − 1 ] [l_2,l_2+2^k-1] [l2,l2+2k1]相等,并且 [ r 1 − 2 k + 1 , r 1 ] [r_1-2^k+1,r_1] [r12k+1,r1] [ r 2 − 2 k + 1 , r 2 ] [r_2-2^k+1,r_2] [r22k+1,r2]相等。
这样把划分区间,就是logn层,每层的区间长度为20,21,…,每层n段。每次要求就把对应层的区间用并查集合并(二维并查集)。
最后再由上至下挨个合并每层(相当于上一层的标记下放),最后看第0层有多少个连通块就行了。

或者直接在一开始合并的时候就下放,碰到下放的两区间已经在一块的时候就停止,最后直接查询第0层。

每一层最多合并n次,logn层,所以总的复杂度是 O ( ( n l o g n + m ) α ) O((nlogn+m)\alpha) O((nlogn+m)α)

Code:

#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
const int mod = 1e9+7;
int n,m,lg[maxn],f[18][maxn];
int find(int k,int x){return f[k][x]==x?x:f[k][x]=find(k,f[k][x]);}
void merge(int k,int l,int r){
	if((l=find(k,l))!=(r=find(k,r))) f[k][l]=r;
}
vector<int>q[maxn];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
	for(int i=lg[n];i>=0;i--) for(int j=1;j<=n;j++) f[i][j]=j;
	int l1,r1,l2,r2,k;
	while(m--){
		scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
		k=lg[r1-l1+1];
		merge(k,l1,l2),merge(k,r1-(1<<k)+1,r2-(1<<k)+1);
	}
	for(int i=lg[n];i>=1;i--){
		for(int j=1;j<=n;j++) q[j].clear();
		for(int j=1;j<=n;j++){
			printf("%d\n",find(i,j));
			q[find(i,j)].push_back(j);
		}
		for(int j=1;j<=n;j++) if(q[j].size()>1)
			for(int k=1;k<q[j].size();k++)
				merge(i-1,q[j][0],q[j][k]),merge(i-1,q[j][0]+(1<<(i-1)),q[j][k]+(1<<(i-1)));
	}
	int s=0,ans=9;
	for(int i=1;i<=n;i++) if(f[0][i]==i) s++;
	for(int i=1;i<s;i++) ans=ans*10%mod;
	printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值