HDU 3461题解

来源:互联网 发布:网络大电影分账比例 编辑:程序博客网 时间:2024/04/29 15:23

题意:

http://blog.csdn.net/tenlee/article/details/38761111

题解:

根据题意,如果一个可调整的区间都没有的话,答案应该是26的N次方。每当加入一个区间的时候,答案就减少为之前的26分之1(因为该区间的加入使得原本不同的26种情况变得等价了)。因此当有x个“不同的”区间加入进来之后,答案应该为26^(N-x)。

但是还有一些特殊情况需要考虑,一个是同样的区间重复加入是不会改变答案的,这点比较好理解。另一点是如果一个区间可以由其他若干个区间“拼接”而得到,那么它的加入不能改变答案。例如如果已经有了区间[1,3]和[4,5],那么再加入区间[1,5]答案也不会改变,因为[1,5]所能实现的密码变化全都可以由同步执行[1,3]与[4,5]来实现,也就是[1,3]+[4,5]等价于[1,5]。特别要注意的是[1,3]+[3,5]这种情况并不等价于[1,5]。

推导到这里,题目的要求就演变成了求所给出的“不同的”区间的数量x,这一步就要靠并查集了。设父节点数组为par[N], par[L] == R+1 代表存在一个区间[L,R]。初始条件下令所有par[i] = -1,每加入一个区间[L,R]则执行Union(L,R+1)操作,若返回值为真(原本不存在[L,R],即 par[L] != R+1),则x增1,否则x不变。由此即可得到最终的x值。

最后在求26^(N-x)时需要用到二分快速幂。

#include<iostream>#include<string.h>#include<stdio.h>using namespace std;const int MaxN = int(1e7+10);const int MUL = 26;const int MOD = int(1e9+7);int par[MaxN];int N,M;int Find(int x);bool Union(int l, int r);int myPower(int n);void init() {    for(int i=0; i<MaxN; i++) {        par[i]=i;    }}int main() {    while(scanf("%d %d",&N,&M) != EOF) {        init();        int cnt = 0;        for(int i = 0; i < M; i++) {            int L,R;            scanf("%d %d",&L,&R);            if(Union(L,R+1)) {                cnt++;    //par[L] == R+1 代表存在一条以L开头以R结尾的线段,此处取R+1是为了实现线段的拼接,换做用(L-1,R)亦可            }        }        printf("%d\n",myPower(N-cnt));    }    return 0;}int Find(int x) {    if(x== par[x]) {        return x;    }    par[x] = Find(par[x]);    return par[x];}bool Union(int l, int r) {    int pl,pr;    pl = Find(l);    pr = Find(r);    if(pl == pr) {        return false;    }    par[pl] = pr;    return true;}int myPower(int n) {    __int64 mul = MUL;    __int64 ans = 1;    while(n > 0) {        if(n&1) {            ans *= mul;            ans %= MOD;        }        n >>= 1;        mul *= mul;        mul %= MOD;    }    return int(ans);}




0 0
原创粉丝点击