其他分享
首页 > 其他分享> > hdu 3461 Code Lock 题解 (思维,并查集)

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

作者:互联网

原题链接:
HDU

题意简述

给定nnn,表示密码长度为nnn。
给定mmm和mmm个区间,每个区间是珂以翻转的,即整体+k+k+k之后是相同的密码。注意,z+1=az+1=az+1=a。比如,如果一个长度为222的密码,111到222珂以翻转,那么ab,bc,cd,dezaab,bc,cd,de\cdots zaab,bc,cd,de⋯za都是相同的密码。

问有多少种不同的密码。对109+710^9+7109+7(即100000000710000000071000000007)取模。

数据

输入

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

输出

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

样例

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

思路

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

那么,kkk是多少呢?

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

4 3
1 2
3 4
1 4

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

那么为什么会出现这种情况呢?原因是有一些区间是没用的。比如说,当我们有了[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;ca&lt;=b&lt;ca<=b<c。我们会发现这个式子有点像等式的\red{传递性}传递性,珂是好像有没有,一个是bbb,一个是b+1b+1b+1。怎么办呢?

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

具体的方法:

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

回到总题解界面

标签:hdu,Code,传递性,int,题解,cnt,密码,lt,区间
来源: https://blog.csdn.net/LightningUZ/article/details/90742625