时限 3 秒,内存 512MB
小 R 喜欢研究机器人。
最近,小 R 新研制出了两种机器人,分别是 P
型机器人和 Q
型机器人。现在他要测试这两种机器人的移动能力,测试在从左到右排成一排的 n n n 个柱子上进行,柱子用 1 − n 1 - n 1−n 依次编号, i i i 号柱子的高度为一个正整数 h i h_i hi。机器人只能在相邻柱子间移动,即:若机器人当前在 i i i 号柱子上,它只能尝试移动到 i − 1 i - 1 i−1 号和 i + 1 i + 1 i+1 号柱子上。
每次测试,小 R 会选取一个起点 s s s,并将两种机器人均放置在 s s s 号柱子上。随后它们会按自己的规则移动。
P
型机器人会一直向左移动,但它无法移动到比起点 s s s 更高的柱子上。更具体地,P
型机器人在 l ( l ≤ s ) l (l \leq s) l(l≤s) 号柱子停止移动,当且仅当下列两个条件均成立:
l = 1 l = 1 l=1 或 h l − 1 > h s h_{l-1} > hs hl−1>hs。
对于满足 l ≤ j ≤ s l \leq j \leq s l≤j≤s 的 j j j,有 h j ≤ h s h_j \leq h_s hj≤hs。
Q
型机器人会一直向右移动,但它只能移动到比起点 s s s 更低的柱子上。更具体地,Q
型机器人在 r ( r ≥ s ) r (r \geq s) r(r≥s) 号柱子停止移动,当且仅当下列两个条件均成立:
r = n r = n r=n 或 h r + 1 ≥ h s h_{r+1} \geq h_s hr+1≥hs。
对于满足 s < j ≤ r s < j \leq r s<j≤r 的 j j j,有 h j < h s h_j < h_s hj<hs。
现在,小 R 可以设置每根柱子的高度, i i i 号柱子可选择的高度范围为 [ A i , B i ] [A_i, B_i] [Ai,Bi],即 A i ≤ h i ≤ B i A_i \leq h_i \leq B_i Ai≤hi≤Bi。小 R 希望无论测试的起点 s s s 选在哪里,两种机器人移动过的柱子数量的差的绝对值都小于等于 2 2 2。他想知道有多少种柱子高度的设置方案满足要求,小 R 认为两种方案不同当且仅当存在一个 k k k,使得两种方案中 k k k 号柱子的高度不同。请你告诉他满足要求的方案数模 1 0 9 + 7 10^9 + 7 109+7 后的结果。
第一行一个正整数 n n n,表示柱子的数量。
接下来 n n n 行,第 i i i 行两个正整数 A i , B i A_i, B_i Ai,Bi,分别表示 i i i 号柱子的最小和最大高度。
仅一行一个整数,表示答案模 1 0 9 + 7 10^9 + 7 109+7 的值。
5
3 3
3 3
3 4
2 2
3 3
1
您可以通过附加文件获得更多样例。
见附加文件的 robot/robot2.in
与 robot/robot2.ans
。
见附加文件的 robot/robot3.in
与 robot/robot3.ans
。
见附加文件的 robot/robot4.in
与 robot/robot4.ans
。
柱子高度共两种情况:
高度为:3 2 3 2 3
。此时若起点设置在 5 5 5,P
型机器人将停在 1 1 1 号柱子,共移动 4 4 4 个柱子。Q
型机器人停在 5 5 5 号柱子,共移动 0 0 0 个柱子,不符合条件。
高度为:3 2 4 2 3
。此时无论起点选在哪,都满足条件,具体见下表:
起点编号 | P 型机器人 | Q 型机器人 |
---|---|---|
1 1 1 | 停在 1 1 1 号柱子,移动过 0 0 0 个 | 停在 2 2 2 号柱子,移动过 1 1 1 个 |
2 2 2 | 停在 2 2 2 号柱子,移动过 0 0 0 个 | 停在 2 2 2 号柱子,移动过 0 0 0 个 |
3 3 3 | 停在 1 1 1 号柱子,移动过 2 2 2 个 | 停在 5 5 5 号柱子,移动过 2 2 2 个 |
4 4 4 | 停在 4 4 4 号柱子,移动过 0 0 0 个 | 停在 4 4 4 号柱子,移动过 0 0 0 个 |
5 5 5 | 停在 4 4 4 号柱子,移动过 1 1 1 个 | 停在 5 5 5 号柱子,移动过 0 0 0 个 |
对于所有测试数据: 1 ≤ n ≤ 300 1 \leq n \leq 300 1≤n≤300 , 1 ≤ A i ≤ B i ≤ 1 0 9 1 \leq A_i \leq B_i \leq 10^9 1≤Ai≤Bi≤109。
每个测试点的具体限制见下表:
测试点编号 | n ≤ n\le n≤ | 特殊性质 |
---|---|---|
1 , 2 1,2 1,2 | 7 7 7 | A i = B i , B i ≤ 7 A_i=B_i,B_i\le 7 Ai=Bi,Bi≤7 |
3 , 4 3,4 3,4 | 7 7 7 | B i ≤ 7 B_i\le 7 Bi≤7 |
5 , 6 , 7 5,6,7 5,6,7 | 50 50 50 | B i ≤ 100 B_i\le 100 Bi≤100 |
8 , 9 , 10 8,9,10 8,9,10 | 300 300 300 | B i ≤ 1 0 4 B_i\le 10^4 Bi≤104 |
11 , 12 11,12 11,12 | 50 50 50 | A i = 1 , B i = 1 0 9 A_i=1,B_i=10^9 Ai=1,Bi=109 |
13 , 14 , 15 13,14,15 13,14,15 | 50 50 50 | 无 |
16 , 17 16,17 16,17 | 150 150 150 | 无 |
18 , 19 18,19 18,19 | 200 200 200 | 无 |
20 20 20 | 300 300 300 | 无 |
#include
using namespace std;
inline int read()
{
int res=0; bool f=0;
char ch=getchar();
while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
while(isdigit(ch)) res=res*10+(ch^'0'),ch=getchar();
return f?-res:res;
}
const int N=605,mod=1000000007;
struct node{
int l,r;
bool operator<(const node &t)const{return r-l<t.r-t.l;}
}p[3030];
int n,tot,id[N][N],a[N],b[N];
int fac[N],inv[N];
inline int qmi(int x,int y)
{
int res=1;
while(y)
{
if(y&1) res=1ll*res*x%mod;
x=1ll*x*x%mod;
y>>=1;
}
return res;
}
void init(int n)//预处理阶乘和逆元
{
fac[0]=inv[0]=1;
for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
inv[n]=qmi(fac[n],mod-2);
for(int i=n-1;i>=1;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
}
inline void add(int &x,int y)
{
x+=y;
if(x>=mod) x-=mod;
}
void dfs(int l,int r)//预处理合法区间
{
if(id[l][r]||l>r) return;
id[l][r]=++tot;
p[tot]={l,r};
for(int i=l;i<=r;i++)//枚举分界线
if(abs((i-l)-(r-i))<=2)
dfs(l,i-1),dfs(i+1,r);
}
int num[N],cnt;
void read_and_change()
{
for(int i=1;i<=n;i++)
{
num[++cnt]=a[i]=read();
num[++cnt]=b[i]=read()+1;//左闭右开 后面方便
}
sort(num+1,num+cnt+1);
cnt=unique(num+1,num+cnt+1)-num-1;
for(int i=1;i<=n;i++)
{
a[i]=lower_bound(num+1,num+cnt+1,a[i])-num;
b[i]=lower_bound(num+1,num+cnt+1,b[i])-num;
}
}
int f[3030][N],pre[N],suf[N];
inline void lag(int l,int r,int len)
{
if(len<=n+1)
{
for(int i=1;i<=tot;i++) f[i][0]=f[i][len];
return;
}
for(int i=1;i<=tot;i++) f[i][0]=0;
pre[0]=suf[n+2]=1;//pre、suf就是把带入len之后的式子的分子前后缀前
for(int i=1;i<=n+1;i++) pre[i]=1ll*pre[i-1]*((len-i+mod)%mod)%mod;
for(int i=n+1;i>=1;i--) suf[i]=1ll*suf[i+1]*((len-i+mod)%mod)%mod;
for(int i=1;i<=n+1;i++)
{
int val=1ll*pre[i-1]*suf[i+1]%mod/*分子*/*inv[n+1-i]%mod*inv[i-1]%mod/*分母*/*(((n+1-i)&1)?mod-1:1)%mod/*分母的符号*/;
for(int j=1;j<=tot;j++)
add(f[j][0],1ll*val*f[j][i]%mod);
}
}
int main()
{
n=read();
init(n+5);
dfs(1,n); sort(p+1,p+tot+1);
read_and_change();//读入并离散化
for(int i=0;i<=n+1;i++) f[0][i]=1;
for(int t=1;t<cnt;t++)
{
int len=min(n+1,num[t+1]-num[t]);
for(int i=1;i<=tot;i++)
{
int l=p[i].l,r=p[i].r;
for(int j=l;j<=r;j++)
if(abs((j-l)-(r-j))<=2&&t>=a[j]&&t<b[j])
for(int k=1;k<=len;k++)
add(f[id[l][r]][k],1ll*f[id[l][j-1]][k]*f[id[j+1][r]][k-1]%mod);
//最初的DP中的“最大值”就是最靠右的最大值,所以右区间的最大值一定比k小(j位置是k)
for(int k=1;k<=len;k++)
add(f[id[l][r]][k],f[id[l][r]][k-1]);//前缀和优化
}
lag(num[t],num[t+1],num[t+1]-num[t]);
for(int i=1;i<=tot;i++)
for(int j=1;j<=len;j++)
f[i][j]=0;
}
printf("%d\n",f[id[1][n]][0]);
return 0;
}
#include
#define il inline
#define re register
#define b_s basic_string
#define For(i,a,b) for(re int i=(a);i<=(b);++i)
#define foR(i,a,b) for(re int i=(a);i>=(b);--i)
#define uint unsigned int
#define ll long long
#define ull unsigned long long
using namespace std;
il void rd(int &x){
x=0;bool f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=0;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
x=f?x:-x;
return;
}
const int maxn=307,maxm=2608;//maxm是最大区间数
const ll mod=1e9+7;
int n;
int A[307],B[307];
il ll qpow(ll x,ll t){
int ret=1;
while(t){
if(t&1) ret=ret*x%mod;
x=x*x%mod;
t>>=1;
}
return ret;
}
il int inv(int x){return qpow(x,mod-2);}
int id[maxn][maxn],toti;//0就是区间不存在
il bool ok(int l,int mid,int r){return abs((mid-l)-(r-mid))<=2;}
il void sinte(int l,int r){//本函数是用于区间初始化的递归函数。
if(id[l][r]) return;
id[l][r]=++toti;
if(l>=r) return;
For(mid,l,r)
if(ok(l,mid,r))
sinte(l,mid-1),sinte(mid+1,r);
}
int key[607],totk;
ll invfact[307];
int usek;
il void init(){//本函数负责读入,并参与到区间初始化和拉插初始化中。
rd(n); usek=n+1;
For(i,1,n){
rd(A[i],B[i]);
key[++totk]=A[i],key[++totk]=B[i]+1;//A[i]是到这里就变,B[i]是下一个才变。我们希望key里面存的每一个都是变的点,这样一来我们就可以取出左闭右开区间。
}
sort(key+1,key+1+totk);
totk=unique(key+1,key+1+totk)-key-1;//去重后结果。
sinte(1,n);
invfact[0]=1;
For(i,1,usek) invfact[i]=invfact[i-1]*inv(i)%mod;
}
int L,R,S,N;//当前考虑的闭区间的左右端点,该区间长度,计划中要dp出的长度
ll dp[maxn][maxm]; bitset<maxm> vis;//该区间是否在本轮dp中访问过
//dp[mx][now]表示第now个区间的最大值不超过mx的方案数。定义tp[mx][now]表示恰好为的方案数。
//对于非初始区间(r>l):
//因为我们在dfs内实际上是逐个区间转移,我们可以先这样转移:dp(实为tp)[mx][now]=∑mid [ (∑i=0~mx tp[i][left])*(∑i=0~mx-1 tp[i][right]) ]=∑mid (dp[mx][left]*dp[mx-1][right])
//然后我们对dp前缀和,因为dp[mx][now]=∑i=1~mx tp[i][now]。不用关心i=0,因为对于非初始区间那里一定是0(A[i]>=1)(更实质地,只有虚区间会用到那个)
il void dfs(int l,int r){
re int now=id[l][r]; if(vis[now]) return; vis[now]=1;
if(l>r){
For(i,0,N)
dp[i][now]=1;
return;
}
if(l==r){
if(A[l]<=L && R<=B[l])
For(i,1,N)
dp[i][now]=1;
}
if(l<r){
For(mid,l,r)
if(ok(l,mid,r)){
dfs(l,mid-1); dfs(mid+1,r);
if(A[mid]<=L && R<=B[mid])
For(i,1,N)
dp[i][now]=(dp[i][now] + dp[i][id[l][mid-1]] * dp[i-1][id[mid+1][r]]) % mod;
}
}
For(i,1,N){
dp[i][now]=dp[i-1][now]+dp[i][now];
if(dp[i][now]>=mod) dp[i][now]-=mod;
}
return;
}
ll son[maxn],sonqian[maxn],sonhou[maxn];
ll mu[maxn],invmu[maxn];
il void work(){
for(re int i=1;i<totk;++i){
L=key[i],R=key[i+1]-1,S=R-L+1;
if(S<=usek){
N=S;
dfs(1,n);
For(i,1,toti) dp[0][i]=dp[N][i];//每次都把上一段的结果放在0处,此时的1就代表L,相当于平移了整个dp数组
}
else{
N=usek;
dfs(1,n);
//首先我们处理分子 son 及其前后缀积。 拉插的代入值 x 应当为区间末端 R 。
sonqian[0]=sonhou[usek+1]=1;
For(j,1,usek){//因为这里1代表L,所以rj(j为1~usek的任意值)=j+L-1
son[j]=S-j;//实际的式子是son[j]=R-rj=R-L+1-j=S-j,由else知S>usek>=j,所以不用判负,而且 R<=1e9 ,所以也不用模
sonqian[j]=sonqian[j-1]*son[j]%mod;
}
foR(j,usek,1) sonhou[j]=sonhou[j+1]*son[j]%mod;
//然后我们来处理分母
For(i,1,usek){
if(((usek-i)&1)==0) invmu[i]=invfact[i-1]*invfact[usek-i]%mod;
else invmu[i]=mod-invfact[i-1]*invfact[usek-i]%mod;
}
for(re int i=1;i<=usek;++i){
ll fenzi=sonqian[i-1]*sonhou[i+1]%mod;
ll xishu=fenzi*invmu[i]%mod;
For(now,1,toti)//暂时放在这里
dp[N+1][now]=(dp[N+1][now]+xishu*dp[i][now])%mod;
}
For(now,1,toti) dp[0][now]=dp[N+1][now],dp[N+1][now]=0;
}
vis.reset();
For(i,1,N)
For(j,1,toti)
dp[i][j]=0;//清空dp。
}
}
int main(){
init();
work();
wt(dp[0][1]);
return 0;
}