题目链接
题意:
有一个长度为n的数,一知l1到r1和l2到r2一样,求这样的数字有多少个。
分析:
除了相等没有别的限制,我们只需要让该相等的相等,求出有多少个可以自由分配的,其中有一个是要分配1-9剩下的分配0-9就好了,怎么处理相等呢?并查集就非常适合,但是问题就是区间长度可以很大,会t掉,所以怎么办呢?线段树,每错,线段树应该是可以解决这个问题,不过有没有更简单的办法,当然有了,其实直接倍增处理就行(不还是线段树吗。。。),当然有点类似吧。f[i][j]表示以i为起点,数1<<j个数,这些数的父亲,可是这些数不一定是一个父亲啊。没关系,这里只记录第一个的父亲,但是这样倍增合并之后并没办法判断有多少个集合,我们还需要都push_down到j==0的那一层,然后再处理就好了。快速幂写不写无所谓啦。
看代码:
#include <cstdio> const int maxn=1e5+10; const int mod=1e9+7; int f[maxn][21]; int pow(int a){ int s=10; int ans=1; while(a){ if(a&1) ans=((long long)ans*(long long)s)%mod; s=((long long)s*(long long)s)%mod; a>>=1; } return ans; } int Find(int a,int b){ return a==f[a][b]?a:(f[a][b]=Find(f[a][b],b)); } int n; void Co(int a,int b,int c){ if(a>n||b>n) return; if(Find(a,c)!=Find(b,c)) f[f[a][c]][c]=f[b][c]; } int main(){ int m; scanf("%d%d",&n,&m); for(int j=0;j<=20;j++) for(int i=1;i<=n;i++) f[i][j]=i; int js1,js2,js3,js4; for(int i=1;i<=m;i++){ scanf("%d%d%d%d",&js1,&js2,&js3,&js4); for(int j=20;j>=0;j--) if(js1+(1<<j)-1<=js2){ Co(js1,js3,j); js1+=(1<<j); js3+=(1<<j); } } for(int j=20;j>=1;j--) for(int i=1;i<=n;i++){ Co(i,Find(i,j),j-1); Co(i+(1<<(j-1)),Find(i,j)+(1<<(j-1)),j-1); } int ans=0; for(int i=1;i<=n;i++) if(f[i][0]==i) ans++; printf("%d",(9ll*(long long)pow(ans-1))%mod); return 0; }