HUD 6044 Limited Permutation

HDU 6044 Limited Permutation

原题连接
http://acm.hdu.edu.cn/showproblem.php?pid=6044

在开始之前。提供一种加速读取的方法。
由于题目输入规模很大。所以建议使用下面代码来读入
利用 fread() 批量读入内存。(下文中代码实现就是使用这个读入)
struct Io
{
    const char l='0'-1;
    const char r='9'+1;
    char A[MAXN];
    char *ptop,*pend;
    Io()
    {
        ptop=pend=A;
    }
    void Io_fread()
    {
        pend=A+fread(A,1,MAXN,stdin);
        ptop=A;
    }
    bool read(int &a)//返回false表示什么也没读到
    {
        if(ptop==pend)Io_fread();
        if(ptop==pend)return false;
        while(*ptop==' '||*ptop=='\n'||*ptop=='\r'||*ptop=='\t')
        {
            ptop++;
            if(ptop==pend)
            {
                Io_fread();
                if(ptop==pend)return false;
            }
        }
        a=0;
        while(*ptop>l&&*ptop<r&&ptop<pend)
        {
            a=a*10+(*(ptop++)-'0');
            if(ptop==pend) Io_fread();
        }
        return true;
    }
}I;

<script type="math/tex; mode=display" id="MathJax-Element-4"></script>

开始正题。读题有可能忽略掉一个很重要的条件:
当且仅当 liLiRri 时。
min(PL,PL+1,...PR)=Pi
当且仅当。意味着 LR 不满足上面条件时。
min(PL,PL+1,...PR)Pi
其实。如果没有忽略这样一个重要的条件。题目还是很有想头的。
<script type="math/tex; mode=display" id="MathJax-Element-9"></script>
那么对于上文条件。我们 R ri+1时。
min(PL,PL+1,...Pri+1)Pi
又:
min(PL,PL+1,...Pri)=Pi
则:
min(PL,PL+1,...Pri+1)=Pri+1

这意味着:
[li,ri][lri+1,ri]

但是单纯从上面的方法出发对题目理解不够全面。
换个角度。
题目用。当且仅当这个词。那意味着。我们随意指定一个 [L,R]
有且仅有一个 i 满足:
i[L,R],[L,R][li,ri]
题目开始变的简单。为什么?
既然任意指定 L,R 都应找到一个 i

那么意味着:

必然存在覆盖[1,n] i

那么对于这样的一个i

也必然有覆盖: [1,i1] , [i+1,n] 的限制区间。
似乎 DFS , BFS 都可以解决。
因为我们得到了一个子问题:

我们记子区间 [L,R] 对应的答案为:
slove(L,R)

<script type="math/tex; mode=display" id="MathJax-Element-29"></script>
因为任意指定 [L,R] 都有且仅有一个 i 满足:
i[L,R],[L,R][li,ri]
所以如果存在 [lt,rt] 有: i[lt,rt] , [lt,rt][li,ri]
则: slove(li,ri)=0
如果不存在某 lt=li,rt=i1
slove(li,ri)=0
因为开始的时候。 answer=slove(1,n)
因为 [1,n] 是全集。所以不存在不属于 [1,n] 的区间。与 [1,n] 有交。
<script type="math/tex; mode=display" id="MathJax-Element-42"></script>

归纳总结有:如果 answer>0 .则不存在这样的 i,j 有:

liljrirj

首先:

lu=1,ru=n , slove(1,n)>0 .由之前的结论有:
不存在这样的 i 有:liuri
那么对于任意 i 。有:[li,ri][1,u1]或者 [li,ri][u+1,n]
那么对于任意 t[1,u1] 都有: [lt,rt][1,u1]
任意 t[u+1,n] 都有: [lt,rt][u+1,n]

其次:

记: lp=1,rp=u1 或者 lp=u+1,rp=n
slove(lp,rp)>0 ,则不存在这样的 t 有:
p[lt,rt] , [lt,rt][lp,rp]
因为任意 t[1,u1] 都有: [lt,rt][1,u1]
因为任意 t[u+1,n] 都有: [lt,rt][u+1,n]
slove(lp,rp) 是原问题的一个子问题。
当前区间。除了 p 以外 , 在这个区间内的元素对应的区间要么属于[lp,p1]或者属于 [p+1,rp]

那么有:

slove(li,ri)=slove(li,i1)slove(i+1,ri)(riliili)

解释:计算完左右子树后。位置 i 必须是最小值。但左右子树互不影响。

其中。两个slove()是左右两边排列方案数。

(riliili) 是左右两边分配方案数。应用乘法乘法原理得到上式。

我们可以建立一棵区间包含关系的树。
根节点为区间 [1,n]
root 的左孩子 [1,u1]
root 的右孩子 [u+1,n]
其他节点依次类推。直到节点对应一个值
如果对于数组 l[],r[] 可以成功建立上述区间包含关系的树。那意味着答案有解。
如果不能建立或者建立的树不合法。则 answer=0
我们可以对区间排序。来建立这棵区间包含关系的树。
建议使用桶排序。由于输入时间较长。系统自带 sort 不确定是否可过。
当然。也可以不建立树。直接 DFS (但排序不可少)
也可以使用单调栈。

下面是 AC 代码

#include <stdio.h>
#include <string.h>
#include <algorithm>
#define MAXN 1000006

using namespace std;
typedef long long LL;
const int P=1e9+7;
struct Io
{
    const char l='0'-1;
    const char r='9'+1;
    char A[MAXN];
    char *ptop,*pend;
    Io()
    {
        ptop=pend=A;
    }
    void Io_fread()
    {
        pend=A+fread(A,1,MAXN,stdin);
        ptop=A;
    }
    bool read(int &a)
    {
        if(ptop==pend)Io_fread();
        if(ptop==pend)return false;
        while(*ptop==' '||*ptop=='\n'||*ptop=='\r'||*ptop=='\t')
        {
            ptop++;
            if(ptop==pend)
            {
                Io_fread();
                if(ptop==pend)return false;
            }
        }
        a=0;
        while(*ptop>l&&*ptop<r&&ptop<pend)
        {
            a=a*10+(*(ptop++)-'0');
            if(ptop==pend) Io_fread();
        }
        return true;
    }
}I;

int tmp[MAXN];

void Sort(int *A,int *B,int *ton,int *Rank,int size)//桶排序。先比较A属性。后比较B属性。
{
    for(int i=0;i<size;i++) ton[i]=0;
    for(int i=1;i<size;i++) ton[B[i]]++;
    for(int i=1;i<size;i++) ton[i]+=ton[i-1];
    for(int i=size-1;i;i--) tmp[ton[B[i]]--]=i;//对B属性排序

    for(int i=0;i<size;i++) ton[i]=0;
    for(int i=1;i<size;i++) ton[A[i]]++;
    for(int i=1;i<size;i++) ton[i]+=ton[i-1];
    for(int i=size-1;i;i--) Rank[ton[A[tmp[i]]]--]=tmp[i];
}

int L[MAXN];
int R[MAXN];
int chL[MAXN];
int chR[MAXN];
int fact[MAXN]={1,1};
int Ifact[MAXN]={1,1};

int ton[MAXN];
int Rank[MAXN];

int Q[MAXN];
int ans[MAXN];
int slove(int);

int main ()
{
    for(int i=2;i<MAXN;i++) Ifact[i]=P-(int)((LL)(P/i)*Ifact[P%i]%P);
    for(int i=1;i<MAXN;i++)
    {
        Ifact[i]=(LL)Ifact[i-1]*Ifact[i]%P;
        fact[i]=(LL)fact[i-1]*i%P;
    }
    int n,c=1;
    while(I.read(n))
    {
        int size=n+1;
        for(int i=1;i<size;i++) I.read(L[i]);
        for(int i=1;i<size;i++) I.read(R[i]);
        Sort(L,R,ton,Rank,size);
        for(int i=1;i<size;i++)
        {
            if(L[Rank[i]]==L[Rank[i-1]])    chL[Rank[i]]=Rank[i-1];
            else    chL[Rank[i]]=0;
        }
        Sort(R,L,ton,Rank,size);
        for(int i=n-1; i ;i--)
        {
            if(R[Rank[i]]==R[Rank[i+1]])    chR[Rank[i]]=Rank[i+1];
            else    chR[Rank[i]]=0;
        }
        chR[Rank[n]]=0;
        printf("Case #%d: %d\n",c++,slove(size));
    }
    return 0;
}

int slove(int size)
{
    int rt=0,n=size-1;
    for(int i=1;i<size;i++)
        if(L[i]==1&&R[i]==n)
        {
            rt=i;
            break;
        }
    if(!rt)return 0;
    int l=0,r=1;
    Q[0]=rt;

    while(l<r)
    {
        int v=Q[l++];

        if(chL[v]>0)
        {
            if(R[chL[v]]!=v-1)return 0;
            Q[r++]=chL[v];
        }
        else if(L[v]!=v) return 0;

        if(chR[v]>0)
        {
            if(L[chR[v]]!=v+1  )return 0;
            Q[r++]=chR[v];
        }
        else if(R[v]!=v)return 0;
    }

    while(r--)
    {
        int v=Q[r];
        int cl=chL[v];
        int cr=chR[v];
        if(cl==0||cr==0)
        {
            ans[v]=ans[cl]+ans[cr];
            if(ans[v]<1)ans[v]=1;
            continue;
        }
        int n=R[v]-L[v];
        ans[v]=(LL)ans[cl]*ans[cr]%P;
        ans[v]=(LL)ans[v]*fact[n]%P*Ifact[v-L[v]]%P*Ifact[R[v]-v]%P;
    }
    return ans[rt];
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值