hdu3461 Code Lock(并查集+快速幂)

7 篇文章 0 订阅
1 篇文章 0 订阅
Code Lock

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)
Total Submission(s): 2029    Accepted Submission(s): 783


Problem Description
A lock you use has a code system to be opened instead of a key. The lock contains a sequence of wheels. Each wheel has the 26 letters of the English alphabet 'a' through 'z', in order. If you move a wheel up, the letter it shows changes to the next letter in the English alphabet (if it was showing the last letter 'z', then it changes to 'a').
At each operation, you are only allowed to move some specific subsequence of contiguous wheels up. This has the same effect of moving each of the wheels up within the subsequence.
If a lock can change to another after a sequence of operations, we regard them as same lock. Find out how many different locks exist?


Input
There are several test cases in the input.

Each test case begin with two integers N (1<=N<=10000000) and M (0<=M<=1000) indicating the length of the code system and the number of legal operations. 
Then M lines follows. Each line contains two integer L and R (1<=L<=R<=N), means an interval [L, R], each time you can choose one interval, move all of the wheels in this interval up.

The input terminates by end of file marker.


Output
For each test case, output the answer mod 1000000007


Sample Input
1 1
1 1
2 1
1 2


Sample Output
1
26


Author
hanshuai
这道题的题意怎么说呢,就是有一个密码锁,有n个转动的轮子,
这些轮子都最多可以从'a'撞到'z',
现在给你一些特定的区间序列[l,r],意思是这个区间的转轮要一起同时转动。
如果转动出现的结果与其他方法转动出现的结果一样,那么他们就视为一把同密码所。
例如:给你区间[1,5],你可以同时转动区间1~5,出现的结果与你想转1,再转2...再转动5之后的结果是一样的,
那么他们就视为同一把锁。
那么怎么做这道题呢,拿就要用到并差集了。
首先,我们把每个转轮看成一个区间,那么密码锁就有26^n种。
接着,就要用到并查集了。
首先呢,1~5是一个集合,那么我们这个区间的密码锁数其实就是26^(5-1),这个应该都清楚吧。
我们现在就是要统计有多少个转动的区间。
怎么用并查集呢。
首先,给你区间[2,4],[4,5]和[2,3],[4,5],虽然他们有转动的都在[2,5],可是对于[2,4]和[4,5]这两个区间,4这个位置一次转动两次,而[2,5]只转动了一次而已,所以这是三个可转动的区间。
如果给你[2,3],[4,5]和[2,5],[2,3]和[4,5]转动的结果其实和[2,5]是一样的,所以[2,3]+[4,5]等同于[2,5],这里其实就只有二个可转动的区间。
像[2,3],[4,5],[2,5]这样的可以用到并差集把大区间[2,5]去掉。
具体为什么用merge(l,r+1),其实就是很好懂,[2,4],[4,6],[2,6],那么(2,4,6)不就在同个集合里面了吗,
其他区间也这样处理,不会受到影响。merge(l-1,r)也是可以的。
最后结果就是(26^(N-Mx))%MOD,这里(N-Mx)太大,我们可以用快速幂来解决。
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<ctime>
#include<string>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#include<set>
#include<map>
#include<cstdio>
#include<limits.h>
#define MOD 1000000007
#define fir first
#define sec second
#define fin freopen("/home/ostreambaba/文档/input.txt", "r", stdin)
#define fout freopen("/home/ostreambaba/文档/output.txt", "w", stdout)
#define mes(x, m) memset(x, m, sizeof(x))
#define Pii pair<int, int>
#define Pll pair<ll, ll>
#define INF 1e9+7
#define inf 0x3f3f3f3f
#define Pi 4.0*atan(1.0)

#define lowbit(x) (x&(-x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define max(a,b) a>b?a:b

typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-9;
const int maxn = 10000000+5;
const int maxm = 10000005;
using namespace std;

inline int read(){
    int x(0),f(1);
    char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}

int p[maxn];
int ans;
inline int find(int x){
    return x==p[x]?x:p[x]=find(p[x]);
}
void merge(int x,int y){
    x=find(x);
    y=find(y);
    if(x!=y){
        p[y]=x;
        --ans;
    }
}
inline ll pow(int x){
    ll res=1;
    if(0==x){
        return res;
    }
    ll n=26;
    while(x){
        if(x&1){
            res=(res*n)%MOD;
        }
        n=(n*n)%MOD;
        x>>=1;
    }
    return res;
}
int main(){
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        ans=n;
        for(int i=1;i<=n+1;++i){
            p[i]=i;
        }
        int l,r;
        while(m--){
            cin>>l>>r;
            merge(l,r+1);
        }
        printf("%lld\n",pow(ans));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值