萌萌哒,题解

题目链接

题意:

  有一个长度为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;
}
原文地址:https://www.cnblogs.com/wish-all-ac/p/12826728.html