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;
}