51nod 1934 受限制的排列 分治+数学

题意

对于一个 1 到 n 的排列 p1,p2,⋯,pn ,我们可以轻松地对于任意的 1≤i≤n 计算出 (li,ri) ,使得对于任意的 1≤L≤R≤n 来说 min(pL,pL+1,⋯,pR)=pi 当且仅当 li≤L≤i≤R≤ri 。
给定整数 n 和 (li,ri) (1≤i≤n) ,你需要计算有多少种可能的 1 到 n 的排列 p1,p2,⋯,pn 满足上述条件。
由于答案可能很大,你只需要给出答案对 10^9+7 取模的值。
n 10 6

分析

辣鸡题目居然卡常,然而并没有松过去,最后只好交一个别人的程序过掉算了。
题目并不难,注意到这个区间实际上就是对这个排列建笛卡尔树后的中序遍历,那么分治算一下然后乘一下组合数就好了。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<map>
#define mp(x,y) std::make_pair(x,y)

typedef long long LL;
typedef std::pair<int,int> pi;

const int N=1000005;
const int MOD=1000000007;

int n,l[N],r[N],jc[N],ny[N];
std::map<pi,int> ma;

int read()
{
    int x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int C(int n,int m)
{
    return (LL)jc[n]*ny[m]%MOD*ny[n-m]%MOD;
}

int solve(int l,int r)
{
    if (l>r) return 1;
    int x=ma[mp(l,r)];
    if (!x) return 0;
    return (LL)solve(l,x-1)*solve(x+1,r)%MOD*C(r-l,x-l)%MOD;
}

int main()
{
    int T=0;
    while (scanf("%d",&n)!=EOF)
    {
        T++;
        printf("Case #%d: ",T);
        ma.clear();
        jc[0]=jc[1]=ny[0]=ny[1]=1;
        for (int i=2;i<=n;i++) jc[i]=(LL)jc[i-1]*i%MOD,ny[i]=(LL)(MOD-MOD/i)*ny[MOD%i]%MOD;
        for (int i=2;i<=n;i++) ny[i]=(LL)ny[i-1]*ny[i]%MOD;
        for (int i=1;i<=n;i++) l[i]=read();
        for (int i=1;i<=n;i++) r[i]=read();
        for (int i=1;i<=n;i++) ma[mp(l[i],r[i])]=i;
        printf("%d\n",solve(1,n));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_33229466/article/details/80454627