• 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
    ",ans);
    }
    
  • 相关阅读:
    基础技术
    Luogu1438 无聊的数列(线段树)
    树状数组从入门到入土
    左偏树
    PA2014-Final Zarowki(堆)
    BZOJ1455罗马游戏
    【小米oj】 海盗分赃
    【小米oj】 最少交换次数
    【小米oj】 大胃王的烦恼
    【小米oj】 不一样的排序
  • 原文地址:https://www.cnblogs.com/anyixing-fly/p/12823952.html
Copyright © 2020-2023  润新知