正题
题目链接:https://www.luogu.com.cn/problem/P3295
题目大意
一个nnn位的数字,mmm个条件给出两个完全相同的区间,求可能的数字数量。
解题思路
其实就是区间中的每个数字分别连边,但是这样显然会TTT。考虑通过消耗查询的复杂度来平衡询问的复杂度。
考虑用STSTST表进行优化,我们定义fi,jf_{i,j}fi,j的点表示位置[i,i+2j−1][i,i+2^j-1][i,i+2j−1]这个区间,显然只有jjj相同的可以进行连边。
连边完成之后我们按照区间长度从大到小的拆分,对于每个点,我们让它的两个下层节点与它父节点的两个下层节点分开连边就好了。
时间复杂度O(nlogn)O(n\log n)O(nlogn)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10,XJQ=1e9+7;
int n,m,cnt,f[N][20],p[N*20],fa[N*20],lg;
int find(int x)
{return fa[x]==x?(x):(fa[x]=find(fa[x]));}
void unionn(int x,int y){x=find(x);y=find(y);if(x==y)return;if(x<y)fa[y]=x;else fa[x]=y;return;
}
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)for(int j=0;i+(1<<j)-1<=n;j++)f[i][j]=++cnt,p[cnt]=i,lg=max(lg,j),fa[cnt]=cnt;while(m--){int l1,r1,l2,r2;scanf("%d%d%d%d",&l1,&r1,&l2,&r2);int len=r1-l1+1;for(int i=0;(1<<i)<=len;i++)if((len>>i)&1)unionn(f[l1][i],f[l2][i]),l1+=(1<<i),l2+=(1<<i);}for(int j=lg;j>=1;j--)for(int i=1;i+(1<<j)-1<=n;i++){int x=f[i][j],y=find(x);if(x==y)continue;int ii=p[y];unionn(f[i][j-1],f[ii][j-1]);unionn(f[i+(1<<j-1)][j-1],f[ii+(1<<j-1)][j-1]);}int ans=1;bool flag=1;for(int i=1;i<=n;i++)if(p[find(f[i][0])]==i){if(flag)ans=9,flag=0;else ans=1ll*ans*10%XJQ;}printf("%d\n",ans);
}