题意:给定i从1到n个区间左右端点,描述的第i个区间的最小值必须是第i个数字,问从1~n有多少种符合题意的排列。
题目提示输入使用快速的fread()
对于一个确定的区间
对于用到的组合数结果,使用卢卡斯预处理出结果。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6+10;
const int mod = 1e9+7;
int n;
int l[N], r[N];
map<pair<int ,int>, int> mp;
LL ans;
LL fac[N], inv[N];
inline char nc()
{
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
template <class T>
inline bool scan_d(T & x)
{
char c=nc();
x=0;
if(c==EOF)return false;
for(; c>'9'||c<'0'; c=nc());
for(; c>='0'&&c<='9'; x=x*10+c-'0',c=nc());
return true;
}
void init()
{
fac[0]=1;
for(int i = 1; i < N; i++)
fac[i] = (fac[i-1]*i) % mod;
inv[0] = inv[1] = 1;
for(int i=2; i<N; i++)
inv[i]=(LL)(mod-mod/i)*inv[mod%i]%mod;
for(int i=1; i<N; i++)
inv[i]=(inv[i-1]*inv[i])%mod;
}
LL C(int n,int m)
{
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
void dfs(int ll,int rr)
{
if(ans==0 || rr<ll) return;
int i = mp[make_pair(ll, rr)];
if(i == 0)
{
ans=0;
return;
}
if(ll == rr)return;
int len = rr - ll;
ans = (ans*C(len,i-ll)) % mod;
dfs(ll, i-1);
dfs(i+1, rr);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("input.txt", "r", stdin);
#endif // ONLINE_JUDGE
init();
for(int Case = 1; scan_d(n); ++Case)
{
mp.clear();
for(int i = 1; i <= n; ++i)
scan_d(l[i]);
for(int i = 1; i <= n; ++i)
{
scan_d(r[i]);
mp[make_pair(l[i], r[i])] = i;
}
ans = 1;
dfs(1, n);
printf("Case #%d: %I64d\n", Case, ans);
}
return 0;
}