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