hdu 3461 Code Lock 题解 (思维,并查集)

本文介绍了HDU 3461 Code Lock问题的解题思路,涉及密码长度n和可翻转区间m。通过分析发现,答案与区间翻转次数有关,利用并查集解决无效区间的问题,最终通过快速幂计算26的cnt次方模1000000007的结果。
摘要由CSDN通过智能技术生成

原题链接:
HDU

题意简述

给定 n n n,表示密码长度为 n n n
给定 m m m m m m个区间,每个区间是珂以翻转的,即整体 + k +k +k之后是相同的密码。注意, z + 1 = a z+1=a z+1=a。比如,如果一个长度为 2 2 2的密码, 1 1 1 2 2 2珂以翻转,那么 a b , b c , c d , d e ⋯ z a ab,bc,cd,de\cdots za ab,bc,cd,deza都是相同的密码。

问有多少种不同的密码。对 1 0 9 + 7 10^9+7 109+7(即 1000000007 1000000007 1000000007)取模。

数据

输入

多组数据。对于每个数据,
第一行有两个正整数 n , m ( n &lt; = 1 e 7 , m &lt; = 1 e 3 ) n,m(n&lt;=1e7,m&lt;=1e3) n,m(n<=1e7,m<=1e3),表示区间长度和珂翻转的区间个数。
接下来 m m m行每行两个正整数 l , r l,r l,r,描述一个珂以翻转的区间 [ l , r ] [l,r] [l,r]

输出

对于每个数据,输出答案。

样例

输入
1 1
1 1
2 1
1 2
输出
1
26

思路

我们会发现,答案肯定是 2 6 k 26^k 26k。因为每翻转一个区间,答案都是不断的除 26 26 26的。

那么, k k k是多少呢?

开始我们会有这样一个猜测: k = n − m k=n-m k=nm。但是过会我们就被打脸了,而且是被自己打的:看这组数据

4 3
1 2
3 4
1 4

答案应该是 2 6 2 26^2 262。珂是按上面那个算法跑出来是 2 6 1 26^1 261。再说,不同的区间数应该会有 n ( n + 1 ) / 2 n(n+1)/2 n(n+1)/2个,拿 n n n减去它。。。估计会跑出来个 26 26 26的负几次方。。。

那么为什么会出现这种情况呢?原因是有一些区间是没用的。比如说,当我们有了 [ 1 , 2 ] [1,2] [1,2] [ 3 , 4 ] [3,4] [3,4]后, [ 1 , 4 ] [1,4] [1,4]就没用了。那么,如何维护这个关系呢?

找到通用形式: [ a , b ] , [ b + 1 , c ] [a,b],[b+1,c] [a,b],[b+1,c]得出 [ a , c ] [a,c] [a,c],其中 a &lt; = b &lt; c a&lt;=b&lt;c a<=b<c。我们会发现这个式子有点像等式的 传 递 性 \red{传递性} ,珂是好像有没有,一个是 b b b,一个是 b + 1 b+1 b+1。怎么办呢?

我们把它弄成 前 开 后 闭 区 间 \red{前开后闭区间} 不就好了?然后就变成了 [ a − 1 , b ] , [ b , c ] [a-1,b],[b,c] [a1,b],[b,c]得出 [ a − 1 , c ] [a-1,c] [a1,c]。我们发现,这太具有传递性了。搞个并查集,就过了。

具体的方法:

  1. 一开始设 c n t = n cnt=n cnt=n
  2. 每次合并区间(前开后闭)的时候,记得判一下是否已经在同一个集合里。如果不在,那么 − − c n t --cnt cnt
  3. 最后用快速幂求出 2 6 c n t % 1000000007 26^{cnt}\%1000000007 26cnt%1000000007

代码:

#include<cstdio>
using namespace std;
namespace Flandle_Scarlet
{
    #define ll long long
    #define N 10000001
    #define mod 1000000007
    int n,m;
    ll cnt;
    class DSU//并查集
    {
        public:
            int Father[N];
            void Init()
            {
                for(int i=0;i<=n+5;i++)
                {
                    Father[i]=i;
                }
            }
            int Find(int x)
            {
                return (x==Father[x])?x:(Father[x]=Find(Father[x]));
            }
            void Merge(int x,int y)//懒得写按秩合并了
            {
                int ax=Find(x),ay=Find(y);
                if (ax!=ay)
                {
                    --cnt;
                    Father[ax]=ay;
                }
            }
    }D;
    ll qpow(ll a,ll b,ll m)//快速幂
    {
        ll r=1;
        while(b)
        {
            if (b&1) r=r*a%m;
            a=a*a%m,b>>=1;
        }
        return r;
    }
    void Input()
    {
        cnt=n;
        for(int i=1;i<=m;++i)
        {
            int l,r;scanf("%d%d",&l,&r);
            D.Merge(l-1,r);
            //前开后闭
        }
        printf("%lld\n",qpow(26,cnt,mod));
    }

    void IsMyWife()
    {
        if (0)
        {
            freopen("","r",stdin);
            freopen("","w",stdout);
        }
        while(scanf("%d%d",&n,&m)==2)
        {
            D.Init();
            Input();
        }
    }
    #undef ll //long long
    #undef N //10000001
    #undef mod //1000000007
};
int main()
{
    Flandle_Scarlet::IsMyWife();
    return 0;
}

回到总题解界面

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值