[SCOI2016]萌萌哒-题解

  • 【题目地址】

题目大意看原题面。

我们首先考虑,对于每个相等的数,我们用并查集将其并起来,那么由于不能有前导0,令最后集合的个数为 c c c,所以答案就是 9 × 1 0 c − 1 9\times 10^{c-1} 9×10c1,除了开头不能选0,只有9中选择方案外,其余的每个数字都有10种选择方案(只有一位数字的时候需要特判,因为此时0也算)。

由于暴力的合并的话,最坏的复杂度会达到 O ( n m l o g n ) O(nmlogn) O(nmlogn),就只有30分,那么由于每次都并的一整块,所以我们用倍增的方式,将一块一块二进制拆分后并起来,那么每次合并只需 O ( l o g 2 n ) O(log^2n) O(log2n)的时间,然后由于最后我们需要知道每一个元素属于哪个集合,所以在从大到小的将块拆开,推下去,最后就可以得知每个元素的所属集合,复杂度 O ( n l o g n ) O(nlogn) O(nlogn),所以最后看一下有多少个集合即可,最终复杂度为 O ( m l o g 2 n + n l o g n ) O(mlog^2n+nlogn) O(mlog2n+nlogn)

大的块拆分的方式如下:

[SCOI2016]萌萌哒-题解_第1张图片

代码:

#include
#include
#include
#define ll long long
using namespace std;
const int mod=1e9+7,Log=22;
const int M=1e5+10;
int n,m;
int f[M][Log];
int find(int a,int dis){return f[a][dis]==a?a:f[a][dis]=find(f[a][dis],dis);}
void init(){for(int i=0;i<Log;i++)for(int j=1;j<=n;j++)f[j][i]=j;}
void merge(int a,int b,int dis){
    int f1=find(a,dis),f2=find(b,dis);
    if(f1!=f2)f[f[a][dis]][dis]=f[b][dis];
}
ll ans=9ll;
ll fpow(ll a,ll b){
	ll res=1;
	for(;b;b>>=1,a=(a*a)%mod)
		if(b&1)res=(res*a)%mod;
	return res;
}
int l1,l2,r1,r2;
void file(){
    freopen("moe.in","r",stdin);
    freopen("moe.out","w",stdout);
}
void close(){
    fclose(stdin);
    fclose(stdout);
}
int cnt,now;
int main(){
    file();
    scanf("%d%d",&n,&m);
    if(n==1){puts("10");return 0;}
    init();
    for(int i=1;i<=m;i++){
        scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        for(int j=Log-1;j>=0;j--){
            if(l1+(1<<j)-1<=r1)merge(l1,l2,j),l1+=(1<<j),l2+=(1<<j);
        }
    }
    for(int j=Log-1;j;j--){
        for(int i=1;i+(1<<j)-1<=n;i++){
            merge(i,find(i,j),j-1);
            merge(i+(1<<(j-1)),f[i][j]+(1<<(j-1)),j-1);
        }
    }
    for(int i=1;i<=n;i++)if(find(i,0)==i) cnt++;
    ans=ans*fpow(10,cnt-1)%mod;
    printf("%lld\n",ans);
    close();
    return 0;
}

你可能感兴趣的:(题解)