HDU - 6044 - Limited Permutation(fread输入挂+计数)

题目链接:HDU - 6044 - Limited Permutation

放一个博客:HDU-6044 (计数) - 十分残念的博客 - CSDN博客
题意是给定 n 个区间 [li,ri] pi [L,R] 中最小的数字当且仅当 liLiRri 。求出有多少种排列满足上述要求。

对于一个区间 [li,ri] pi 是其中最小的数,因此区间还可分为 [li,i1] [i+1,ri] ,其中 pj pk 分别为其中最小的数字。
因此将所有区间按 li 递增, ri 递减的顺序排序。第一个区间一定要是 [1,n] ,然后可以分成区间 [1,i1] [i+1,n] ,其中 [1,i1] 是第 i+1 个区间,然后继续递归下去。(这种递归过程形象的看就像是在线段树上进行先序遍历)。
然后将数字进行分配。
假设 f(i) 为第 i 个区间的方案数,那么 f(i)=f(j)f(k)Cilirili
答案为 f([1,n])

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=1e6+7;
int n;
ll frac[N],inv[N];
struct Seg
{
    int l,r,id;
    bool operator < (const Seg& t) const
    {
        return l<t.l||(l==t.l&&r>t.r);
    }
}seg[N];
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;
    }
}
int cur;
bool ok;

ll power(ll a, ll b)
{
    ll c=1;
    while(b)
    {
        if(b&1) c=c*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return c;
}

void init()
{
    frac[0]=1;
    for(int i=1;i<N;++i) frac[i]=frac[i-1]*i%mod;
    for(int i=1;i<N;++i) inv[i]=power(frac[i],mod-2);
}

ll C(ll n, ll m)
{
    if(m==0||m==n) return 1;
    return frac[n]*inv[m]%mod*inv[n-m]%mod;
}
ll dfs(int l, int r)
{
    if(!ok) return 0;
    if(l>r) return 1;
    if(seg[cur].l!=l||seg[cur].r!=r)
    {
        ok=false;
        return 0;
    }
    int id = seg[cur++].id;
    ll res=dfs(l,id-1)*dfs(id+1,r)%mod*C(r-l,id-l)%mod;
    return res;
}
int main()
{
    int T=1;
    init();
    IO::begin();
    while(IO::read(n))
    {
        for(int i=0;i<n;++i)
            IO::read(seg[i].l);
        for(int i=0;i<n;++i)
            IO::read(seg[i].r),seg[i].id=i+1;
        sort(seg,seg+n);
        cur=0;
        ok=true;
        printf("Case #%d: %I64d\n",T++,dfs(1,n));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值