http://acm.hdu.edu.cn/showproblem.php?pid=3461
题意:给出一个由N NN个字母组成的锁。给出M MM个区间[L,R] [L,R][L,R],每次操作可以将某个区间中所有字母变为字典序中的下一个字母。特殊地,‘z’会变成’a’。如果一把锁通过对可操作区间的有限次操作可以得到另一个锁,那么认为这两个锁是相同的。请求出一共有多少种不同的锁%(1e9+7)
解法:就是慢慢推广。
- 一个区间都没有,不同的方案数就是26^n
- 只有一个区间[1,1],不同的方案数应该是26^(n-1)
- 只有一个区间[1,3],不同的方案数还是26^(n-1)。为啥呢?假设第一位开始是a,我们可以通过旋转时的这一位变成任意的字符。那前三位不同的锁的情况是多少?是第2位的26*第3位的26。只有第一位的计算跳过去了。
- 对于不相交区间[1,3] [4,5]这个肯定是26^(n-2)
- 对于相交的区间[1,3][3,5],第2位需要将结果*26,第三位是不需要的。因为假设第三位一开始为a,可以通过旋转[3,5]区间使得第3位任意的变化。所以结果还是26(n-2)
- 对于区间[1,3],[4,5][1,5]。[1,5]可以通过[1,3][4,5]的操作来实现。所以[1,5]是不需要的。当然[1,4][3,5][1,5]这样的三个区间还都是有效的。
- 所以结果就是26^(n-cnt)%mod cnt就是无效的区间的个数
问题就转换成了找到[1,3][4,5][1,5]这样的区间,然后去掉[1,5]。记录[1,5]这样区间的个数cnt。结果就是26^(n-cnt)%mod
怎样记录呢?非常巧妙的用了并查集!unit(l,r+1) 注意是r+1。然后判重就行。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const ll mod=1e9+7;
const int maxn=1e7+5;
int fa[maxn];
int find(int x){
return x==fa[x]?x:(fa[x]=find(fa[x]));
}
ll mpow(ll x,ll n,ll mod){//x是底数 n是指数 mod是模数
ll ans=1;
while(n){
if(n&1) ans=ans*x%mod;
x=x*x%mod;
n=n>>1;
}
return ans;
}
int main()
{
int n,m;
while(EOF!=scanf("%d%d",&n,&m)){
for(int i=1;i<=n+1;i++) fa[i]=i;
int cnt=n-m,l,r;
for(int i=1;i<=m;i++){
scanf("%d%d",&l,&r);
r++;
l=find(l),r=find(r);
if(l==r){
cnt++;
continue;
}
fa[l]=r;
}
printf("%lld\n",mpow(26,cnt,mod));
}
return 0;
}