SCOI萌萌哒 并查集

题目链接

分析

话说今天的两道题好像都是SCOI的?
题意就是给定多个限制条件,让你求满足条件的数。
第一眼看成了数位DP,发现不可行,因为可能每个位置都要枚举一遍,于是就会发现,其实每个位置0-9都可以放,除了首位,还有每个限制位置的数字应该相等,不难想到并查集+乘法计数原理。
初始的时候,每个位置都是一个单独的集合,每次加限制条件的时候把区间内对应的点合并就行,这样只需要统计出集合的个数\(n\),然后最后答案就是\(9*10^{n-1}\),9是首位不能放0去掉了一个数字,关键是这样做会T掉。
要怎么优化呢?这个我的确也没想到,去百度了一下竟然用的是倍增?
这道题怎么用倍增的思想呢?和稀疏表一样,这样就相当于合并了整段区间,这样就省去了很多遍历的时间。
但问题就回来了,合并大区间的时候是方便了,我们查询的时候怎么办,查询的时候不可能去考虑每个大区间,而是单个的点,那么我们就需要考虑将每个大区间的合并下放到小区间。
其实也跟稀疏表那个一样,分成两个区间就行了,然后把这个区间上的点都合并到大区间的根上,然后就完了。
感觉这个题自己想,可能真的够呛,因为这个倍增实在是太女少口阿了。

#include<cstdio>
#define ll long long
const int N=1e5+10,Mod=1e9+7;
int f[N][25];
int find(int x,int y){
    return f[x][y]==x?x:(f[x][y]=find(f[x][y],y));
}
void Mer(int x,int y,int l){
    f[find(x,l)][l]=find(y,l);
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int j=0;j<=20;j++)
        for(int i=1;i<=n;i++)
            f[i][j]=i;
    for(int i=1;i<=m;i++){
        int a,b,c,d;
        scanf("%d%d%d%d",&a,&b,&c,&d);
        for(int j=20;j>=0;j--)
            if(a+(1<<j)-1<=b)
                Mer(a,c,j),a+=1<<j,c+=1<<j;
    }
    for(int j=20;j;j--)
        for(int i=1;i+(1<<j)-1<=n;i++)
            Mer(i,find(i,j),j-1),Mer(i+(1<<j-1),f[i][j]+(1<<j-1),j-1);
    int cnt=0;
    ll ans=9;
    for(int i=1;i<=n;i++)
        if(find(i,0)==i)cnt++;
    for(int i=1;i<cnt;i++){
        ans*=10;
        ans%=Mod;
    }
    printf("%lld\n",ans);
}
posted @ 2020-05-03 22:22  An_Fly  阅读(120)  评论(0编辑  收藏  举报