Limited Permutation
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 1293 Accepted Submission(s): 339
Problem Description
As to a permutation p1,p2,⋯,pn from 1 to n, it is uncomplicated for each 1≤i≤n to calculate (li,ri) meeting the condition that min(pL,pL+1,⋯,pR)=pi if and only if li≤L≤i≤R≤ri for each 1≤L≤R≤n.
Given the positive integers n, (li,ri) (1≤i≤n), 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 1≤n≤106.
The second line contains n positive integers l1,l2,⋯,ln, satisfying 1≤li≤i for each 1≤i≤n.
The third line contains n positive integers r1,r2,⋯,rn, satisfying i≤ri≤n for each 1≤i≤n.
It's guaranteed that the sum of n in all test cases is not larger than 3⋅106.
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
Source
2017 Multi-University Training Contest - Team 1
还是一样,读题很重要……
本题的破题关键点在于区间满足的条件:if and only if(当且仅当)。所以说,对于一个数字i,它是区间[li,ri]的最小值,这个li和ri不能扩大或者缩小,即a[li-1]和a[ri+1]都比a[i]小。有了这个我们就可以知道,所有的区间,要么是相互包含的,要么没有交集,不会出现有相交的情况。这个如何解释呢?我们们用反证法,假设区间[li,ri]与[lj,rj]相交,对于li<lj<ri<rj的情况,根据当且仅当的条件,我们可以知道a[ri+1]<a[i],又a[j]<a[ri+1],所以a[j]<a[i],所以lj<=li,区间包含,故与区间相交矛盾,所以不可能出现相交。另外的情况同理可证。
知道这个以后,我们就可以尝试虚拟的建立一棵笛卡尔树。首先对区间按照左端点升序,右端点降序排序。根据笛卡尔树性质,笛卡尔树的先序遍历就是数字从小到大的排序,按照刚刚排好的区间顺序就是一个序列,按照先序依次放上区间即可。然后方案数的话,我们设dp[i]表示以i为根的笛卡尔树,标号为1~sz[i]的满足条件的方案数个数,那么有转移方程dp[i]=dp[lson]*dp[rson]*c(sz[i],sz[lson]),其中sz[i]表示以i为根的子树的大小。为什么要乘多一个组合数呢?这个其实很容易理解,dp[lson]和dp[rson]表示的只是标号为1~sz[lson]或sz[rson]的方案数,现在我总共可以取的标号有sz[i]种,那么从sz[i]任意取sz[lson]个标号都能构成一种不重复的标号,所以总共的不重复的方案数还要再乘上一个c(sz[i],sz[lson])。
注意到数字比较大(100W),所以我们还要用上费马小确定的阶乘逆元。最后还要注意,输入非常的大,文件大小有38M左右,所以要用读入挂fread,这个比普通的读入优化还要快,这个之间我还不会用,在之前读入优化的版上改了改,就当收了个模板吧。具体见代码:
#include<bits/stdc++.h>
#define mod 1000000007
#define LL long long
#define N 1001000
using namespace std;
namespace IO //超级读入挂
{
const int len=4e7;char buf[len];int sz,p; //4e7大概大小为44M,可以放得下38M的输入
void begin(){p=0;sz=fread(buf,1,len,stdin);}
inline bool read(LL &x)
{
if (p==sz)return 0;int f=1,d=0;char s=buf[p++];
while(s<'0'||s>'9'&&p<sz){if(s=='-') f=-1;s=buf[p++];}
while(s>='0'&&s<='9'&&p<sz){d=d*10+s-'0';s=buf[p++];}
x=f*d; return p!=sz;
}
inline void writeln(LL x)
{
if(x==0){putchar('0');putchar('\n');return;}
if(x<0)putchar('-'),x=-x;int len=0,buf[20];
while(x)buf[len++]=x%10,x/=10;int i=len-1;
while (i>=0){putchar(buf[i--]+'0');}putchar('\n');return;
}
}
struct seg {LL l,r,id;} a[N];
bool cmp(seg a,seg b)
{
return a.l==b.l?a.r>b.r:a.l<b.l;
}
LL f[N],n,m; bool flag;
LL fastpow(LL x,LL n)
{
LL ret=1;
while (n)
{
if (n&1) ret=ret*x%mod;
x=x*x%mod; n>>=1;
}
return ret;
}
LL c(LL n,LL m) //求组合数
{
LL ret=1;
ret=ret*f[n]*fastpow(f[m]*f[n-m]%mod,mod-2)%mod;
return ret;
}
LL dfs(LL l,LL r) //dfs虚拟建笛卡尔树
{
if (l>r) return 1LL; //如果区间左端点大于右端点,说明父亲没有该儿子
seg now=a[++m];
if (now.l!=l||now.r!=r||flag) //如果树区间合法,但不存在这种区间,说明矛盾
{
flag=1; return 0LL;
} if (l==r) return 1LL; //叶子节点只有一种方案
LL res=c(now.r-now.l,now.id-now.l)*dfs(now.l,now.id-1)%mod; //按标号分左右儿子,分别计算
res=res*dfs(now.id+1,now.r)%mod;
return res;
}
int main()
{
IO::begin();
int T_T=0; f[0]=1;
for(int i=1;i<N;i++)
f[i]=f[i-1]*(LL)i%mod;
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+1+n,cmp);
printf("Case #%d: ",++T_T);
m=0; flag=0;
IO::writeln(dfs(1,n));
}
}