Description
一排可以转动的密码锁,现在给你很多个区间,你可以操作同时转动这个区间内的密码锁,能够通过转动使之一样的密码锁为一种密码
如可以操作2~4这个区间:那么ABBB 和AAAA是一个密码
问你这个密码锁总共有多少种不同的密码
思路:
如果这题没有转动限制的话,那么答案很明显,就是(n为区间大小)26的n次方,但是由于可以转动所以有些锁的状态会重复,那么同一个状态重复了多少次呢!!!!设有cnt个可以自由移动的区间,那么重复次数就是26的cnt次方(想一想为什么,和数学的排列组合很像),那么除以重复次数就可以了
结果是26的(n-cnt)次方种不同的状态锁,显然要用到快速幂
现在最大的问题就是怎么求独立区间个数,
模拟过程:
现在有一个独立区间A[1,4]和B[5,8],现在多出来一个C区间[1,9],显然C任何一种状态都可由转动AB这两个得到,那么C的加入并没有增加新的独立区间,此时C=A+B,同理C=A-B也没有增加新的独立区间
容易发现没有增加新的独立区间的C有一个特点,C区间的两端已经"连在"了一起.
而这种是通过并查集实现的!!!只想说为什么这么神奇!!!!!每次增加一个区间的 时候就把区间左端点和右端点+1联合起来,如果一个新增加的区间左端和右端+1并没有在一个集合,说明这是一个独立区间,否则不是,就这样统计独立区间个数
然后用快速幂就好了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<cstdlib>
#include<algorithm>
#include<stack>
#include<map>
#include<queue>
#include<vector>
using namespace std;
const int maxn = 1e7+100;
const int MOD = 1000000007;
#define pr(x) cout << #x << " = " << x << " ";
#define prln(x) cout << #x << " = " << x <<endl;
#define ll long long
int p[maxn];
int findset(int x) {
return p[x] = x == p[x]? x: findset(p[x]);
}
bool unite(ll x, ll y) {
x = findset(x), y = findset(y);
if(x == y) return false;
p[x] = y;
return true;
}
ll fun(ll x, ll m) {
ll ans = 1;
while(m) {
if(m & 1) ans = ans * x % MOD;
x = x * x % MOD;
m >>= 1;
}
return ans % MOD;
}
int main(){
#ifdef LOCAL
//freopen("C:\\Users\\User Soft\\Desktop\\in.txt","r",stdin);
//freopen("C:\\Users\\User Soft\\Desktop\\out.txt","w",stdout);
#endif
ll m, n, l, r, cnt;
while( scanf("%lld%lld", &n, & m) == 2) {
cnt = n;
for(int i = 0; i <= n; ++i) p[i] = i;
for(int i = 0; i < m; ++i) {
scanf("%lld%lld", &l, &r);
cnt -= unite(l - 1, r);
}
cout << (fun(26,cnt)+MOD)%MOD << "\n";
}
return 0;
}