1. 程式人生 > >HDU 6044--Limited Permutation(搜索+組合數+逆元)

HDU 6044--Limited Permutation(搜索+組合數+逆元)

name review cst pri getchar value ane ips begin

題目鏈接

Problem Description As to a permutation p1,p2,?,pn from 1 to n, it is uncomplicated for each 1in to calculate (li,ri) meeting the condition that min(pL,pL+1,?,pR)=pi if and only if liLiRri for each 1LRn.

Given the positive integers n, (li,ri) (1in)
, you are asked to calculate the number of possible permutations p1,p2,?,pn from 1 to n, meeting the above condition.

The answer may be very large, so you only need to give the value of answer modulo 109+7.

Input The input contains multiple test cases.

For each test case:

The first line contains one positive integer n
, satisfying 1n106.

The second line contains n positive integers l1,l2,?,ln, satisfying 1lii for each 1in.

The third line contains n positive integers r1,r2,?,rn, satisfying irin for each 1in.

It‘s guaranteed that the sum of n in all test cases is not larger than 3106
.

Warm Tips for C/C++: input data is so large (about 38 MiB) that we recommend to use fread() for buffering friendly.
size_t fread(void *buffer, size_t size, size_t count, FILE *stream); // reads an array of count elements, each one with a size of size bytes, from the stream and stores them in the block of memory specified by buffer; the total number of elements successfully read is returned.

Output For each test case, output "Case #x: y" in one line (without quotes), where x indicates the case number starting from 1 and y denotes the answer of corresponding case.

Sample Input 3 1 1 3 1 3 3 5 1 2 2 4 5 5 2 5 5 5

Sample Output Case #1: 2 Case #2: 3 題意:對於一個由1~n組成的排列稱為合法的,必須滿足這樣的條件: 有n個(li,ri),對於每個 i 必須滿足 min(pL,pL+1,?,pR)=pi if and only if liLiRri for each 1LRn.

現在給了n個(li,ri)求滿足的排列有多少個。

思路:對於每個(li,ri),a[li-1]<a[i]>a[ri+1] ,並且a[i]是a[li]~a[ri]的最小值,那麽可以想到:對於區間(1,n)一定有一個最小值,所以一定有一個區間是(1,n)(用X表示),那麽這個最小值把區間X分成兩部分U和V ,所以一定存在為U和V的區間,如果不存在,那麽輸出0,令f(X)表示符合條件的區間X的排列數,那麽f(X)=f(U)*f(V)*C(U+V+1,U) 【註:C()表示組合數,U 表示區間U的大小】,所以我們只需要從區間(1,n)進行深搜即可,其中因為數據太大取模會用到逆元和組合數。

代碼如下:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long LL;
const int N=1e6+5;
const LL mod=1e9+7;
LL fac[N], Inv[N];
/*int Scan()///輸入外掛
{
    int res=0,ch,flag=0;
    if((ch=getchar())==‘-‘)
        flag=1;
    else if(ch>=‘0‘&&ch<=‘9‘)
        res=ch-‘0‘;
    while((ch=getchar())>=‘0‘&&ch<=‘9‘)
        res=res*10+ch-‘0‘;
    return flag?-res:res;
}*/
namespace IO {
    const int MX = 4e7; //1e7占用內存11000kb
    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;
    }
}

void Init(){
    fac[0] = Inv[0] = fac[1] = Inv[1] = 1;
    for(int i=2; i<N; i++) fac[i] = fac[i-1] * i % mod;
    for(int i=2; i<N; i++) Inv[i] = (mod - mod / i) * Inv[mod % i] % mod;
    for(int i=2; i<N; i++) Inv[i] = Inv[i] * Inv[i-1] % mod;
}
LL C(LL n, LL m){
    LL ans = fac[n] * Inv[m] % mod* Inv[n-m] %mod;
    return ans;
}
int ii;
struct Node{
    int l,r;
    int id;
}a[N];
bool cmp(const Node s1,const Node s2)
{
    if(s1.l==s2.l) return s1.r>s2.r;
    return s1.l<s2.l;
}
LL dfs(int L,int R)
{
    if(a[ii].l!=L || a[ii].r!=R)  return 0;
    int m=a[ii++].id;
    LL fL=1,fR=1;
    if(L<=m-1) fL=dfs(L,m-1);
    if(m+1<=R) fR=dfs(m+1,R);
    LL c=C(R-L,m-L);
    return fL*fR%mod*c%mod;
}
int main()
{
    Init();
    int n,Case=1;
    IO::begin();
    while(IO::read(n))
    {
       for(int i=1;i<=n;i++)  IO::read(a[i].l);
       for(int i=1;i<=n;i++)  IO::read(a[i].r), a[i].id = i;
       sort(a+1,a+n+1,cmp);
       ii=1;
       LL ans=dfs(1,n);
       printf("Case #%d: %lld\n",Case++,ans);
    }
    return 0;
}

HDU 6044--Limited Permutation(搜索+組合數+逆元)