【BZOJ4569】萌萌哒

题目链接

BZOJ4569

解法:倍增+并查集

先想暴力的做法。每次读入 l 1 , r 1 , l 2 , r 2 l_1,r_1,l_2,r_2 l1,r1,l2,r2,令 i i i 0 0 0枚举到 r 1 − l 1 r_1-l_1 r1l1,然后用并查集合并 l 1 + i l_1+i l1+i l 2 + i l_2+i l2+i,最后统计并查集中联通块的个数 s s s,答案即为 9 × 1 0 s − 1 9×10^{s-1} 9×10s1
但是这样会TLE。
对暴力进行优化。更改并查集中数组 f f f的含义,令 f x , k f_{x,k} fx,k表示从 x x x x + 2 k − 1 x+2^k-1 x+2k1这两段的共同编号,亦即若 f x , k = f y , k f_{x,k}=f_{y,k} fx,k=fy,k则表示 f x , … , f x + 2 k − 1 f_x,\dots,f_{x+2^k-1} fx,,fx+2k1 f y , … , f y + 2 k − 1 f_y,\dots,f_{y+2^k-1} fy,,fy+2k1这两段数每项分别处于相同的联通块中。合并时用倍增的处理方式(参考ST表)。

代码
#include<iostream>
#include<cstdio>
#include<cmath>

using namespace std;

const long long p=1000000007ll;
long long ans=1ll;
int n,m,l1,l2,r1,r2,f[17][100001];
bool flag=false;

int find(int x,int k){
	return f[k][x]==x?x:f[k][x]=find(f[k][x],k);
}

void merge(int x,int y,int k){
	if(y>100000)return;
	int fx=find(x,k),fy=find(y,k);
	if(fx!=fy)f[k][fx]=fy;
}

void update(int a,int b,int c,int d){
	int k=(int)log2(b-a+1);
	merge(a,c,k),merge(b-(1<<k)+1,d-(1<<k)+1,k);
}

int main(){
	scanf("%d%d",&n,&m);int l=(int)log2(n);
	for(int i=1;i<=n;++i)for(int k=0;k<17;++k)f[k][i]=i;
	for(int i=1;i<=m;++i)scanf("%d%d%d%d",&l1,&r1,&l2,&r2),update(l1,r1,l2,r2);
	for(int i=l;i;--i)for(int j=1;j<=n-(1<<i)+1;++j){int f=find(j,i);merge(j,f,i-1),merge(j+(1<<i-1),f+(1<<i-1),i-1);}
	for(int i=1;i<=n;++i)if(find(i,0)==i)(ans*=(flag?10:((flag=true),9)))%=p;
	printf("%lld",ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值