2017多校联合第一场 1012题 hdu 6044 Limited Permutation 笛卡尔树 递归

34 篇文章 0 订阅
2 篇文章 0 订阅

题目链接


题意:

对于一个 1 ~ n 的排列 a[n],假设 a[i] 为可能的最大的区间 [l, r] 范围内的最小值。

对于每一个 a[i], 都有这样的 l[i] 与 r[i].

现给定 l[i] 与 r[i], 问有多少种符合的排列。


推荐:

http://blog.csdn.net/qq_31759205/article/details/76146845


思路:

对于整个区间内的最小值 a[k], 其左右端点必然分别为 1, n, 其将序列分成了 1 ~ k - 1 与 k + 1 ~ n 两部分;

左半部分的最小值 a[k1], 其左右端点必然分别为 1, k - 1, 其将序列分成了 1 ~ k1 - 1 与 k1 + 1 ~ k - 1 两部分,

右半部分的最小值 a[k2], 其左右端点必然分别为 k + 1, n, 其将序列分成了 k1 + 1 ~ k2 - 1 与 k2 + 1 ~ n 两部分;

……

这样,子问题的性质就很明显了,可以用递归来解决。

不可能构成的情况即为找不到一个区间满足上述的划分。


记 u 为区间 [l, r] 的划分点,左子树为 [l, u - 1], 右子树为 [u + 1, r], f 为某段区间的可能排列数

则 f([l, r]) = f([l, u -1]) * f([u + 1, r]) * C(l - r, u - l).


求组合数时,因为 n 太大,不可能直接求,也不可能递推。

于是又学到了一招:乘法逆元。

详情见:http://blog.csdn.net/lu_1110/article/details/52151335


事实上,用递归是过不了最后五组数据的0 0但是在hdu上能过

大概看了一下数据,最后一组是一个单调减的数列,这样递归的层数太深了显然会爆。

(标程写得太迷了也不想看目前就这样先凑合着吧((叹


AC代码如下:

#include <cstdio>
#include <algorithm>
#include <iostream>
#define maxn 1000000
using namespace std;
typedef long long LL;
LL U[maxn + 10], D[maxn + 10];
const LL mod = 1e9 + 7;
int kas, n, cnt;
bool flag;
template<typename T> inline void readint(T &x) {
    x = 0; T f = 1; char ch = getchar();
    while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); }
    while (isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); }
    x *= f;
}
namespace IO {
    const int MX = 4e7;
    char buf[MX]; int c, sz;
    void begin() {
        c = 0;
        sz = fread(buf, 1, MX, stdin);
    }
    inline bool read(int &t) {
        while(c < sz && buf[c] != '-' && (buf[c] < '0' || buf[c] > '9')) c++;
        if(c >= sz) return false;
        bool flag = 0; if(buf[c] == '-') flag = 1, c++;
        for(t = 0; c < sz && '0' <= buf[c] && buf[c] <= '9'; c++) t = t * 10 + buf[c] - '0';
        if(flag) t = -t;
        return true;
    }
}
struct node {
    int l, r, id;
}a[maxn + 10];
LL poww(LL a, LL b) {
    LL ret = 1;
    while (b) {
        if (b & 1) ret = ret * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ret;
}
void pre() {
    U[0] = D[0] = U[1] = D[1] = 1;
    for (int i = 2; i <= maxn; ++i) U[i] = U[i - 1] * i % mod;
    for (int i = 2; i <= maxn; ++i) D[i] = poww(U[i], mod - 2);
}
bool cmp(node u, node v) {
    return u.l < v.l || (u.l == v.l && u.r > v.r);
}
LL C(LL a, LL b) {
//    printf("%lld %lld\n", a, b);
    return U[a] * D[b] % mod * D[a - b] % mod;
}
LL dfs(int l, int r) {
    if (flag) return 0;
    if (l > r) return 1;
    ++cnt;
    if (a[cnt].l != l || a[cnt].r != r) { flag = true; return 0; }
    if (l == r) return 1;

    int temp = cnt;
    LL x1 = dfs(l, a[temp].id - 1);
    LL x2 = dfs(a[temp].id + 1, r);
    return x1 * x2 % mod * C(a[temp].r - a[temp].l, a[temp].id - a[temp].l) % mod;
}
void work() {
    for (int i = 1; i <= n; ++i) { IO::read(a[i].l); a[i].id = i; }
    for (int i = 1; i <= n; ++i) IO::read(a[i].r);
    flag = false;
    sort(a + 1, a + n + 1, cmp);
    cnt = 0;
    printf("Case #%d: %d\n", ++kas, dfs(1, n));
}
int main() {
    IO::begin();
    pre();
    while (IO::read(n)) work();
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值