标签:
有一个无前导0的n位数,有m个限制形如[l1,r1]=[l2,r2],问满足条件的数有多少种,答案模10^9+7。
我们用ST表,f[i,j]表示[i,i+2^j-1]这一段。
那么初始时每一段单独成一个集合。
对于一个限制可以拆成log 份,然后进行集合合并。
然后呢,如果任意f[s,t]和f[i,j]属于同一集合,那么f[s,t-1]与f[i,j-1]以及f[s+2^(t-1)-1,t-1]和f[i+2^(j-1)-1,j-1]都应该属于同一集合。
为了满足这个限制,只要最后再一层一层的做,把下一层的合并了即可。
合并注意必须让编号大的合进编号小的里。
统计答案就很简单啦,具体看代码。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=100000+10,mo=1000000007,maxd=18;
int fa[maxn*21],id[maxn][21],di[maxn*21][2],two[21];
int i,j,k,l1,r1,l2,r2,s,t,n,m,ans,tot;
int getfa(int x){
return fa[x]?fa[x]=getfa(fa[x]):x;
}
int merge(int x,int y){
x=getfa(x);
y=getfa(y);
if (x>y) swap(x,y);
if (x!=y) fa[y]=x;
}
int main(){
two[0]=1;
fo(i,1,maxd) two[i]=two[i-1]*2;
scanf("%d%d",&n,&m);
if (n==1){
printf("10\n");
return 0;
}
fo(i,1,n)
fo(j,0,maxd)
id[i][j]=++tot,di[tot][0]=i,di[tot][1]=j;
fo(i,1,m){
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
fd(j,maxd,0)
if (l1+two[j]-1<=r1){
merge(id[l1][j],id[l2][j]);
l1+=two[j];l2+=two[j];
}
}
fd(j,maxd,1)
fo(i,1,n){
k=getfa(id[i][j]);
s=di[k][0];t=di[k][1];
merge(id[s][t-1],id[i][j-1]);
merge(id[s+two[t-1]][t-1],id[i+two[j-1]][j-1]);
}
ans=1;
fo(i,1,n)
if (!fa[id[i][0]])
ans=(ll)ans*((i==1)?9:10)%mo;
printf("%d\n",ans);
}
标签:
原文地址:http://blog.csdn.net/werkeytom_ftd/article/details/51334030