原题链接:
HDU
题意简述
给定
n
n
n,表示密码长度为
n
n
n。
给定
m
m
m和
m
m
m个区间,每个区间是珂以翻转的,即整体
+
k
+k
+k之后是相同的密码。注意,
z
+
1
=
a
z+1=a
z+1=a。比如,如果一个长度为
2
2
2的密码,
1
1
1到
2
2
2珂以翻转,那么
a
b
,
b
c
,
c
d
,
d
e
⋯
z
a
ab,bc,cd,de\cdots za
ab,bc,cd,de⋯za都是相同的密码。
问有多少种不同的密码。对 1 0 9 + 7 10^9+7 109+7(即 1000000007 1000000007 1000000007)取模。
数据
输入
多组数据。对于每个数据,
第一行有两个正整数
n
,
m
(
n
<
=
1
e
7
,
m
<
=
1
e
3
)
n,m(n<=1e7,m<=1e3)
n,m(n<=1e7,m<=1e3),表示区间长度和珂翻转的区间个数。
接下来
m
m
m行每行两个正整数
l
,
r
l,r
l,r,描述一个珂以翻转的区间
[
l
,
r
]
[l,r]
[l,r]。
输出
对于每个数据,输出答案。
样例
输入
1 1
1 1
2 1
1 2
输出
1
26
思路
我们会发现,答案肯定是 2 6 k 26^k 26k。因为每翻转一个区间,答案都是不断的除 26 26 26的。
那么, k k k是多少呢?
开始我们会有这样一个猜测: k = n − m k=n-m k=n−m。但是过会我们就被打脸了,而且是被自己打的:看这组数据
4 3
1 2
3 4
1 4
答案应该是 2 6 2 26^2 262。珂是按上面那个算法跑出来是 2 6 1 26^1 261。再说,不同的区间数应该会有 n ( n + 1 ) / 2 n(n+1)/2 n(n+1)/2个,拿 n n n减去它。。。估计会跑出来个 26 26 26的负几次方。。。
那么为什么会出现这种情况呢?原因是有一些区间是没用的。比如说,当我们有了 [ 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 < = b < c a<=b<c a<=b<c。我们会发现这个式子有点像等式的 传 递 性 \red{传递性} 传递性,珂是好像有没有,一个是 b b b,一个是 b + 1 b+1 b+1。怎么办呢?
我们把它弄成 前 开 后 闭 区 间 \red{前开后闭区间} 前开后闭区间不就好了?然后就变成了 [ a − 1 , b ] , [ b , c ] [a-1,b],[b,c] [a−1,b],[b,c]得出 [ a − 1 , c ] [a-1,c] [a−1,c]。我们发现,这太具有传递性了。搞个并查集,就过了。
具体的方法:
- 一开始设 c n t = n cnt=n cnt=n
- 每次合并区间(前开后闭)的时候,记得判一下是否已经在同一个集合里。如果不在,那么 − − c n t --cnt −−cnt。
- 最后用快速幂求出 2 6 c n t % 1000000007 26^{cnt}\%1000000007 26cnt%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 3461 Code Lock问题的解题思路,涉及密码长度n和可翻转区间m。通过分析发现,答案与区间翻转次数有关,利用并查集解决无效区间的问题,最终通过快速幂计算26的cnt次方模1000000007的结果。
471

被折叠的 条评论
为什么被折叠?



