P5469 [NOI2019] 机器人 洛谷黑题题解
[NOI2019] 机器人
题目背景
时限 3 秒,内存 512MB
题目描述
小 R 喜欢研究机器人。
最近,小 R 新研制出了两种机器人,分别是 P 型机器人和 Q 型机器人。现在他要测试这两种机器人的移动能力,测试在从左到右排成一排的 nnn 个柱子上进行,柱子用1−n1 - n1−n 依次编号,iii 号柱子的高度为一个正整数 hih_ihi。机器人只能在相邻柱子间移动,即:若机器人当前在 iii 号柱子上,它只能尝试移动到 i−1i - 1i−1 号和 i+1i + 1i+1 号柱子上。
每次测试,小 R 会选取一个起点 sss,并将两种机器人均放置在 sss 号柱子上。随后它们会按自己的规则移动。
P 型机器人会一直向左移动,但它无法移动到比起点 sss 更高的柱子上。更具体地,P 型机器人在 l(l≤s)l (l \leq s)l(l≤s) 号柱子停止移动,当且仅当下列两个条件均成立:
-
l=1l = 1l=1 或 hl−1>hsh_{l-1} > hshl−1>hs。
-
对于满足 l≤j≤sl \leq j \leq sl≤j≤s 的 jjj,有 hj≤hsh_j \leq h_shj≤hs。
Q 型机器人会一直向右移动,但它只能移动到比起点 sss 更低的柱子上。更具体地,Q 型机器人在 r(r≥s)r (r \geq s)r(r≥s) 号柱子停止移动,当且仅当下列两个条件均成立:
-
r=nr = nr=n 或 hr+1≥hsh_{r+1} \geq h_shr+1≥hs。
-
对于满足 s<j≤rs < j \leq rs<j≤r 的 jjj,有 hj<hsh_j < h_shj<hs。
现在,小 R 可以设置每根柱子的高度,iii 号柱子可选择的高度范围为 [Ai,Bi][A_i, B_i][Ai,Bi],即Ai≤hi≤BiA_i \leq h_i \leq B_iAi≤hi≤Bi。小 R 希望无论测试的起点 sss 选在哪里,两种机器人移动过的柱子数量的差的绝对值都小于等于222。他想知道有多少种柱子高度的设置方案满足要求,小 R 认为两种方案不同当且仅当存在一个 kkk,使得两种方案中 kkk 号柱子的高度不同。请你告诉他满足要求的方案数模 109+710^9 + 7109+7 后的结果。
输入格式
第一行一个正整数 nnn,表示柱子的数量。
接下来 nnn 行,第 iii 行两个正整数 Ai,BiA_i, B_iAi,Bi,分别表示 iii 号柱子的最小和最大高度。
输出格式
仅一行一个整数,表示答案模 109+710^9 + 7109+7 的值。
样例 #1
样例输入 #1
5
3 3
3 3
3 4
2 2
3 3
样例输出 #1
1
提示
更多样例
您可以通过附加文件获得更多样例。
样例 2
见附加文件的 robot/robot2.in 与 robot/robot2.ans。
样例 3
见附加文件的 robot/robot3.in 与 robot/robot3.ans。
样例 4
见附加文件的 robot/robot4.in 与 robot/robot4.ans。
样例 1 解释
柱子高度共两种情况:
-
高度为:
3 2 3 2 3。此时若起点设置在 555,P型机器人将停在 111 号柱子,共移动444 个柱子。Q型机器人停在 555 号柱子,共移动 000 个柱子,不符合条件。 -
高度为:
3 2 4 2 3。此时无论起点选在哪,都满足条件,具体见下表:
| 起点编号 | P 型机器人 | Q 型机器人 |
|---|---|---|
| 111 | 停在 111 号柱子,移动过 000 个 | 停在 222 号柱子,移动过 111 个 |
| 222 | 停在 222 号柱子,移动过 000 个 | 停在 222 号柱子,移动过 000 个 |
| 333 | 停在 111 号柱子,移动过 222 个 | 停在 555 号柱子,移动过 222 个 |
| 444 | 停在 444 号柱子,移动过 000 个 | 停在 444 号柱子,移动过 000 个 |
| 555 | 停在 444 号柱子,移动过 111 个 | 停在 555 号柱子,移动过 000 个 |
数据范围
对于所有测试数据:1≤n≤3001 \leq n \leq 3001≤n≤300 , 1≤Ai≤Bi≤1091 \leq A_i \leq B_i \leq 10^91≤Ai≤Bi≤109。
每个测试点的具体限制见下表:
| 测试点编号 | n≤n\len≤ | 特殊性质 |
|---|---|---|
| 1,21,21,2 | 777 | Ai=Bi,Bi≤7A_i=B_i,B_i\le 7Ai=Bi,Bi≤7 |
| 3,43,43,4 | 777 | Bi≤7B_i\le 7Bi≤7 |
| 5,6,75,6,75,6,7 | 505050 | Bi≤100B_i\le 100Bi≤100 |
| 8,9,108,9,108,9,10 | 300300300 | Bi≤104B_i\le 10^4Bi≤104 |
| 11,1211,1211,12 | 505050 | Ai=1,Bi=109A_i=1,B_i=10^9Ai=1,Bi=109 |
| 13,14,1513,14,1513,14,15 | 505050 | 无 |
| 16,1716,1716,17 | 150150150 | 无 |
| 18,1918,1918,19 | 200200200 | 无 |
| 202020 | 300300300 | 无 |
#include<bits/stdc++.h>
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<bits/stdc++.h>
#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;
}
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)