HDU 6044 Limited Permutation dfs 统计

地址

http://acm.hdu.edu.cn/showproblem.php?pid=6044

题意

给出 n,(li,ri),(1<=i<=n),(1<=n<=106) ,问合法排列的方案数,一个合法的排列满足对 li<=L<=i<=R<=ri ,总有 min(pL,pL+1,...,pR)==pi

思路

对一个区间 [L,R] ,当前区间总有一个最小的数,因为这个数对应的 Li,Ri 会覆盖整个区间,因为这个数是当前区间的最小数,所以这个数会把区间划分成左右两个部分,这两个部分因为被两者之间的最小数隔开,变成了完全独立的子问题,于是递归下去。

当递归到只有一个数时,这个区间返回方案数1(长度为1的排列当然只有1个方案),一个区间的方案数就是其两个子区间的方案数相乘再乘上 C(siz[l]+siz[r],siz[l]) ,因为左右两个区间是独立的子问题,所以把当前区间的数分给左右区间有组合数种方案,左右区间拿到自己的数后,就有自己原有的方案数。

标程用的笛卡尔树的做法可以用 O(n) 的预处理, O(1) 的查询代替使用sort()进行 O(nlogn) 的预处理和使用lower_bound()带来的 O(logn) 的查询。

不难发现,不可能有两个 Li,Ri Lj,Rj 完全相同,而且每个区间一定有一个可以覆盖其整个区间的最小数。反之无解。

代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int IOMX = 4096;

char buf[IOMX], IOt[50];
int bi = IOMX, bn = IOMX;

int read(char *s) {
    while (bn) {
        for (; bi < bn && buf[bi] <= ' '; ++bi);
        if (bi < bn) break;
        bn = fread(buf, 1, IOMX, stdin);
        bi = 0;
    }
    int sn = 0;
    while (bn) {
        for (; bi < bn && buf[bi] > ' '; ++bi) s[sn++] = buf[bi];
        if (bi < bn) break;
        bn = fread(buf, 1, IOMX, stdin);
        bi = 0;
    }
    s[sn] = 0;
    return sn;
}

bool read(int &x) {
    if (!read(IOt)) return false;
    x = atoi(IOt);
    return true;
}

#define lowbit(x) (x & (-x))

typedef long long LL;
const int MAXN = 1e6 + 5;
const int MOD = 1e9 + 7;

struct Node {
  int l, r, pos;
  Node(int l = 0, int r = 0, int pos = 0): l(l), r(r), pos(pos) {}
  bool operator<(const Node &node) {
    return l < node.l || (l == node.l && r < node.r);
  }
  bool operator==(const Node &node) {
    return l == node.l && r == node.r;
  }
};

int n;
int tree_array[MAXN];
LL ans;
LL factorail_num[MAXN], inv_factorail[MAXN];
bool no_solution;
Node nodes[MAXN];

inline LL Pow(LL a, LL b) {
  LL ret = 1;
  for (; b; b >>= 1) {
    if (b & 1) (ret *= a) %= MOD;
    (a *= a) %= MOD;
  }
  return ret;
}

inline LL C(int n, int m) {
  return factorail_num[n] * inv_factorail[m] % MOD * inv_factorail[n - m] % MOD;
}

LL dfs(int l, int r) {
  if (l > r) return 1;
  int idx = lower_bound(nodes, nodes + n, Node(l, r)) - nodes;
  if (idx == n || nodes[idx].l != l || nodes[idx].r != r) no_solution = true;
  if (no_solution) return 0;
  if (l == r) return 1;
  LL ret1 = dfs(l, nodes[idx].pos - 1);
  LL ret2 = dfs(nodes[idx].pos + 1, r);
  return C(r - l, nodes[idx].pos - l) * ret1 % MOD * ret2 % MOD;
}

int main() {
  factorail_num[0] = inv_factorail[0] = 1;
  for (int i = 1; i < MAXN; ++i) {
    factorail_num[i] = (factorail_num[i - 1] * i) % MOD;
    inv_factorail[i] = (inv_factorail[i - 1] * Pow(i, MOD - 2)) % MOD;
  }
//  cout << C(5, 1) << endl;
//  cout << C(6, 3) << endl;
  int kase = 0;
//  while (~scanf("%d", &n)) {
//    for (int i = 0; i < n; ++i) scanf("%d", &nodes[i].l);
//    for (int i = 0; i < n; ++i) scanf("%d", &nodes[i].r);
  while (read(n)) {
    for (int i = 0; i < n; ++i) read(nodes[i].l);
    for (int i = 0; i < n; ++i) read(nodes[i].r);
    for (int i = 0; i < n; ++i) nodes[i].pos = i + 1;
    no_solution = false;
    sort(nodes, nodes + n);
    for (int i = 1; i < n; ++i) if (nodes[i - 1] == nodes[i]) no_solution = true;
    ans = dfs(1, n);
    printf("Case #%d: %I64d\n", ++kase, ans);
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值