这个题非常巧妙,它描述了一种新的延迟标记关系。。
正常的线段树是用n*4的空间描述一个序列,所以对于一段区间,有可能会有log个点对应一个点
而线段树是利用尽量小的空间使查询变为log
st表是nlogn的空间,对于一段区间,是可以做到一一对应的,它只是预处理比线段树暴力了一些
所以这个题要放到线段树上的话就需要对两个区间进行拆分,在线段树的节点上更新father数组
这样散开的点有可能是log^2的,而且给点打标记比较麻烦
而st表的话,由于区间长度是一一对应的,所以只需要打两个标记,重叠的标记用并查集并起来
然后就相当于线段树的枚举单点查询操作了,一层层的pushdown(不是一层层的也可以,不过麻烦)
这个题对于暴力并查集的优化就是对区间也用并查集,用延迟标记的叠加来减少重复操作
码:
#include<iostream>
#include<cstdio>
#define P 1000000007
using namespace std;
int f[100005][25],o,n,m,i,j,er[25],l1,l2,r1,r2,cnt,ans;
int zhao(int o,int x)
{
if(f[o][x]!=o)f[o][x]=zhao(f[o][x],x);
return f[o][x];
}
int main()
{
scanf("%d%d",&n,&m);
er[0]=1;
for(i=1;i<=20;i++)er[i]=er[i-1]*2;
for(i=1;i<=n;i++)
for(j=0;j<=20;j++)
f[i][j]=i;
for(j=1;j<=m;j++)
{
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
int o=r1-l1+1;
for(i=0;i<=20;i++)
if(er[i]&o)
{
int f1=zhao(f[l1][i],i),f2=zhao(f[l2][i],i);
if(f1!=f2)f[f1][i]=f2;
l1+=er[i];
l2+=er[i];
}
}
for(j=20;j>=1;j--)
{
for(i=1;i<=n;i++)
{
//分下去
if(i+er[j]-1>n)continue;
o=zhao(i,j);
int ll=zhao(o,j-1);
int lll=zhao(i,j-1);
f[ll][j-1]=lll;
int rr=zhao(o+er[j-1],j-1);
int rrr=zhao(i+er[j-1],j-1);
f[rr][j-1]=rrr;
}
}
for(i=1;i<=n;i++)
if(zhao(i,0)==i)++cnt;
ans=9;
for(i=1;i<cnt;i++)
ans=1ll*ans*10%P;
printf("%d",ans);
}