您的位置:首页 > 其它

[BZOJ4569][Scoi2016]萌萌哒(并查集+st表)

2016-08-02 20:16 309 查看

题目描述

传送门

题解

考场上没想出来,看了题解之后感觉很厉害呀。

可以发现相等的两个区间中互相对应的位置是联系在一起的,也就是说,确定了一个就可以确定另外一个。所以可以考虑把这样的点合并起来。考场上写了个比较傻逼的tarjan,其实并查集就是可以做的。无向图的tarjan实际上就是并查集。时间复杂度O(nm)

但是这样的话就有很多冗余的合并,因为区间有一些是重复的,所以有的点就被合并了很多次,考虑如何去除重复操作。可以用ST表来实现。f[j][i]的含义是 从i开始的2^j个字符分别和从f[j][i]开始的2^j个字符对应相等。

每次并查集合并f[j][x],f[j][y]的时候,要把f[j-1][x],f[j-1][y] 以及 f[j-1][x+(1<<(j-1))] f[j-1][y+(1<<(j-1)] 合并,这样自顶向下的合并过程中,如果之前已经合并过了就直接退出。

ST表可以表示一段区间,所以这样来合并区间的操作就很方便了。

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define LL long long

const int N=1e5+5;
const int sz=18;
const int Mod=1e9+7;

int n,m,cnt;
int f[sz+5]
;
LL ans;

inline int find(int j,int x)
{
if (x==f[j][x]) return x;
f[j][x]=find(j,f[j][x]);
return f[j][x];
}
inline void merge(int j,int x,int y)
{
int f1=find(j,x),f2=find(j,y);
if (f1==f2) return;
f[j][f1]=f2;
if (!j) return;
merge(j-1,x,y); merge(j-1,x+(1<<(j-1)),y+(1<<(j-1)));//
}

int main()
{
scanf("%d%d",&n,&m);
if (n==1)
{
puts("10");
return 0;
}
for (int j=0;j<sz;++j)
for (int i=1;i<=n;++i)
f[j][i]=i;
int x1,y1,x2,y2;
for (int i=1;i<=m;++i)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
if (x1>x2) swap(x1,x2),swap(y1,y2);
if (x1==x2) continue;
int j=log2(y1-x1+1);
merge(j,x1,x2); merge(j,y1-(1<<j)+1,y2-(1<<j)+1);
}
for (int i=1;i<=n;++i)
if (find(0,i)==i) cnt++;
LL ans=9;
for (int i=1;i<cnt;++i)
ans=(ans*10)%Mod;
printf("%lld\n",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: