• HDU 3461 Code Lock(并查集,合并区间,思路太难想了啊)


    完全没思路,题目也没看懂,直接参考大牛们的解法。

    http://www.myexception.cn/program/723825.html

    题意是说有N个字母组成的密码锁,如[wersdfj],每一位上的字母可以转动,变成字母表中的下一位。 如w可转动变成x,z变成a。但是题目规定,只能同时转动某个区间上的所有字母,如[1,3], 那么第1到第3个的所有字母要同时转动, 那么[wersdfj]经过一次操作就变成[xfssdfj].一共有M个区间是可以操作的。 

    经过可操作区间进行的操作得到的所有情况,都是同一个的。 也就是指不管我在题目给出的某一个区间怎么转动,其它位上的字母都不变,它仍视为同一种。

    假如原本是[abc],给出可操作区间[1,2],即我可以转动第一个、第二个字母,当然是同时转动的。 由于[1,2]区间情况所能变化得到的,都视为同一种。我不妨固定第一个字母,就为a,其余25个字母可有a变化得到。 那么第二位上的字母,就可以有26种可能,也就是[1,2]区间可能的情况有26种,再加上独立的第3个字母有26种可能,总共有26^2种。

    也就是说,有n个字母组成的密码锁,原本会有26^n种。如果有一个可操作区间,那么会有26次变换。 相当于这个操作区间就减少了原来的26种,总的也就变为26^(n-1)。 也就是现在我们要统计有多少个可操作区间,若为cnt,则答案即为26^(n-cnt),这当然要用到快速幂。

    至于怎么求区间个数,采用的是区间合并的方法,给出一个区间[L,R],若可以合并,则cnt++。

    不过这里有一个情况要注意:

    假设有区间[1,3],[3,5],[1,5],如果按照Union(L, R)来算, 那么[1,3],[3,5]可以合并,cnt为2, 但[1,5]因为已经属于同一个集合,则不合并。那么得到两种可操作区间。 但实际上是有3中可操作区间的,因为1~3, 3~5是有重叠了3的两个区间,所以1~5区间的情况不包括在前两个区间的情况内。

    如果是[1,3],[4,5],[1,5],如果合并的话,cnt为3,但实际就只有两种情况,因为最后一种的转动情况包含在了前两种转动的情况之内了。

    这里大牛们就想出了一个解决办法:

    合并[L-1,R]或者[L,R+1]。

    如上述区间[1,3],[3,5],[1,5],[1,3]合并的是[0,3],[3,5]合并的是[2,5],[1,5]合并的是[0,5],这样cnt=3;

    如果是[1,3],[4,5],[1,5],那么cnt=2;

    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn=10000005;
    const int mod=1000000000+7;
    int father[maxn];
    int n,m;
    
    void init(){
        for(int i=0;i<=n;i++)
            father[i]=i;
    }
    
    int find_root(int x){
        if(father[x]!=x)
            father[x]=find_root(father[x]);
        return father[x];
    }
    
    bool Union(int a,int b){
        int x=find_root(a);
        int y=find_root(b);
        if(x==y)
            return false;
        father[y]=x;
        return true;
    }
    
    //注意要long long ,即使a(26)也要定义成long long
    long long quickPow(long long a,int b){
        long long ans=1;
        while(b>0){
            if(b&1){
                ans=(ans*a)%mod;
            }
            a=a*a%mod;
            b=b>>1;
        }
        return ans;
    }
    
    int main()
    {
        int l,r;
        while(scanf("%d%d",&n,&m)!=EOF){
            int cnt=0;
            init();
            for(int i=1;i<=m;i++){
                scanf("%d%d",&l,&r);
                l--;
                if(Union(l,r))
                    cnt++;
            }
            long long ans=quickPow(26,n-cnt);
            printf("%I64d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    (14) go 结构体
    (13) go map
    (12) go make初始化
    (11)go 数组和切片
    (10) go 错误
    (9) go 时间日期
    (8)go 字符串
    (7) go 函数
    (6) go 流程控制
    (5) go 格式化输入输出 类型转换
  • 原文地址:https://www.cnblogs.com/chenxiwenruo/p/3296423.html
Copyright © 2020-2023  润新知