题目大意
一个没有前导零的
n
位数,一共有
求可能的数的个数,结果模
1000000007
。
1≤n,m≤105
题目分析
可以发现约束其实就是一些对应位置相等的限制,我们使用并查集,采用最暴力方法合并,时间复杂度为
O(nm)
。如果最后一共剩下
cnt
个集合,如果某个集合不包含第一位,那么它肯定有
10
种取值,否则只有
9
种。那么答案就是
我们考虑使用
STi,j
表来优化合并。另
STi,j
表示区间
[i,i+2j−1]
所在的集合。一开始每一个
STi,j
都单独一个集合。那么一个约束,就可以转化为
log2n
个集合的相等关系,我们将这些集合一一合并即可。
然后我们就按
j
从大到小的顺序进行集合合并,令
注意我们要判断该集合是否包含位置
总的时间复杂度
O(nα(n)log2n)
。
代码实现
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cctype>
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch))
{
if (ch=='-')
f=-1;
ch=getchar();
}
while (isdigit(ch))
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
const int P=1000000007;
const int N=100500;
const int LGN=17;
int sta[N*LGN],fa[N*LGN];
int n,m,tot,ans;
int ST[N][LGN];
int getfather(int son){return fa[son]==son?son:fa[son]=getfather(fa[son]);}
void merge(int l1,int l2,int j)
{
int f1=getfather(ST[l1][j]),f2=getfather(ST[l2][j]);
if (f1>f2) swap(f1,f2);
if (f1!=f2)
fa[f2]=f1;
}
int main()
{
freopen("cutie.in","r",stdin);
freopen("cutie.out","w",stdout);
n=read(),m=read();
if (n==1)
printf("10\n");
else
{
for (int i=1;i<=n;i++)
for (int j=0;i+(1<<j)-1<=n;j++)
ST[i][j]=++tot,sta[tot]=i,fa[tot]=tot;
for (int i=1,l1,r1,l2,r2;i<=m;i++)
{
l1=read(),r1=read(),l2=read(),r2=read();
for (int j=LGN-1;j>=0;j--)
if (l1+(1<<j)-1<=r1)
{
merge(l1,l2,j);
l1+=1<<j,l2+=1<<j;
}
}
for (int j=LGN-1;j>=1;j--)
for (int i=1;i+(1<<j)-1<=n;i++)
{
int f=getfather(ST[i][j]),i0=sta[f];
merge(i0,i,j-1),merge(i0+(1<<j-1),i+(1<<j-1),j-1);
}
ans=1;
for (int i=1;i<=n;i++)
if (fa[ST[i][0]]==ST[i][0])
ans=ans*1ll*(i==1?9:10)%P;
printf("%d\n",ans);
}
fclose(stdin);
fclose(stdout);
return 0;
}