HUD 6044 Limited Permutation

来源:互联网 发布:网络掉包测试软件 编辑:程序博客网 时间:2024/05/17 08:37

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;

开始正题。读题有可能忽略掉一个很重要的条件:

当且仅当 liLiRri时。

min(PL,PL+1,...PR)=Pi

当且仅当。意味着LR不满足上面条件时。

min(PL,PL+1,...PR)Pi

其实。如果没有忽略这样一个重要的条件。题目还是很有想头的。

那么对于上文条件。我们Rri+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)

因为任意指定[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]有交。

归纳总结有:如果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 1000006using 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