洛谷 P5469 - [NOI2019] 机器人(区间 dp+拉格朗日插值)
神仙题,放在 D1T2 可能略难了一点(
首先显然对于 P 型机器人而言,将它放在 \(i\) 之后它会走到左边第一个严格 \(>a_i\) 的位置,对于 Q 型机器人而言,将它放在 \(i\) 之后它会走到右边第一个 \(\ge a_i\) 的位置,为了避免分类讨论我们可以假定 \(a_0=a_{n+1}=\infty\)。看到这个状态我们可以设计出一个区间 \(dp\),\(dp_{l,r,x}\) 表示 \([l,r]\) 中的柱子最大值为 \(x\),并且有 \(a_{l-1}>x,a_{r+1}\ge x\) 的方案数,再设 \(sum_{l,r,x}\) 表示 \(dp_{l,r,x}\) 的前缀和,那么我们显然可以枚举最靠后的最大值的位置 \(t\),那么对于放在 \(t\) 的 P 型机器人,显然它会走到 \(l-1\),Q 型机器人会走到 \(r+1\),因此 \(dp_{l,r,x}\) 可以从 \(t\) 转移的充要条件是 \(|(t-l)-(r-t)|\le 2\) 且 \(x\in[a_l,a_r]\),因此转移是常数级别的,这样暴力做是 \(\mathcal O(n^2A)\) 的,其中 \(A=\max\{b_i\}\),可以拿到 \(35\) 分的好成绩,不过注意到对于一个区间而言,它只可能从靠中间的位置转移过来,也就是说很多 DP 状态是转移不到 \(dp_{1,n,*}\) 的,打个表可以发现有用的区间最多 \(2518\) 个,因此写个记搜,再卡卡常即可拿到 \(50\) 分。
接下来思考正解,注意到有一档部分分是 \(A_i=1,B_i=10^9\),咱们不妨来当回心理学家(大雾),思索一下,这档部分分有啥用?\(A_i=1,B_i=10^9\) 意味着所有 \(10^9\) 以内的正整数都可以成为柱子的高度,也就是说 \(x\in[a_l,a_r]\) 这个条件就没有用了,因此我们只用关心 \(|(t-l)-(r-t)|\le 2\) 这个条件即可。接下来再思索一下,\(B_i\) 这么大,出题人会让我们干嘛?显然对于 \(l=r\) 时,\(dp_{l,r,x}=1,sum_{l,r,x}=x\),而对于 \(r-l=1\) 的情况,\(dp_{l,r,x}\) 会从 \(sum_{l,l,x}\) 或 \(sum_{r,r,x}\) 转移过来,因此 \(dp_{l,r,x}\) 可以写成 \(kx\) 的形式,\(sum_{l,r,x}\) 也就可以写成 \(Ax^2+Bx\) 的形式……对!多项式!通过上面的观察可以发现,\(dp_{l,r,x}\) 是关于 \(x\) 的 \(r-l\) 次多项式,\(sum_{l,r,x}\) 是关于 \(x\) 的 \(r-l+1\) 次多项式,证明可以归纳。我们可以求出 \(sum_{l,r,x}\) 的任意 \(r-l\) 个点值,然后把这个多项式插出来即可通过 \(11,12\) 这两个测试点,由于点值连续,插值可以线性。
考虑满分做法,既然有了 \(A_i,B_i\) 的限制,并且它们范围这么大,那肯定要离散化咯,我们按照这题的套路将每个区间看作一个左开右闭的区间 \([A_i,B'_i)\),其中显然 \(B'_i=B_i+1\),然后离散化。这样一来一个数 \(i\) 就可以代表一个左开右闭的区间 \([C_i,C_{i+1})\)。按照之前的思路我们猜想在每个这样左开右闭的区间中,\(dp_{l,r,x}\) 是关于 \(x\) 的 \(r-l\) 次多项式,\(sum_{l,r,x}\) 是关于 \(x\) 的 \(r-l+1\) 次多项式,事实上这个猜想也是正确的,读者自证不难。因此考虑从小到大枚举每个区间,然后对每个 \(l,r\),求出该区间中最小的 \(\min(C_{i+1}-C_i,n+1)\) 个数的点值即可,具体实现就每次枚举一个 \(C_i\) 就开个数组 sum[l][r][x] 表示上文中所说的 \(sum_{l,r,C_i+x-1}\),dp[l][r][x] 表示上文中所说的 \(dp_{l,r,C_i+x-1}\),按照上面的套路转移 dp[l][r][x] 即可,求完所有区间的 dp 后对每个区间进行一遍插值,求出 \(sum_{l,r,x}\),在 \(C_{i+1}-1\) 处的点值,然后把它存入新的 sum[l][r][0] 即可,正确性显然,复杂度 \(\mathcal O(2518n^2)\),轻微卡常(不过对于我这个卡常菜鸡来说就得交 \(114514191981019260817998244353\) 发咯),得用一些卡常技巧,这里稍微介绍一下我的卡常技巧:
- 对区间 \([l,r]\) 进行插值的时候,可以只用前 \(r-l+1\) 个点值插值,不用插 \(n+1\) 个点值。
- 可以特判掉 \(C_{i+1}-C_i\le n\) 的情况,这样 \(C_{i+1}-1\) 处的点值我们在 DP 的时候已经求出来了
- 插值不必按部就班地求出前缀后缀积,由于我们已经特判掉了 \(C_{i+1}-C_i\le n\) 的情况,因此一定有 \(C_i+j-1\ne C_{i+1},j\in[1,n+1]\),此时只要预处理一遍每个 \(C_{i}+j-1\) 的逆元然后那全部 \(C_i+j-1\) 的乘积乘上对应的逆元即可。
const int MAXN=300;
const int MOD=1e9+7;
const int MAXS=2520;
void add(int &x,int y){((x+=y)>=MOD)&&(x-=MOD);}
int qpow(int x,int e){
int ret=1;
for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
return ret;
}
int n,a[MAXN+5],b[MAXN+5],key[MAXN*2+5],uni[MAXN*2+5],num=0;
int id[MAXN+5][MAXN+5],itvl_n=0;pii itvls[MAXS+5];
void find_itvl(int l,int r){
if(id[l][r]) return;id[l][r]=++itvl_n;
itvls[itvl_n]=mp(l,r);if(l>r) return;
for(int i=l;i<=r;i++) if(abs((i-l)-(r-i))<=2)
find_itvl(l,i-1),find_itvl(i+1,r);
}
int dp[MAXS+5][MAXN+5];bool vis[MAXN+5][MAXN+5];
void work(int l,int r,int len,int itv){
if(l>r){
for(int i=0;i<=len;i++) dp[id[l][r]][i]=1;
return;
} if(vis[l][r]) return;vis[l][r]=1;
for(int i=1;i<=len;i++) dp[id[l][r]][i]=0;
for(int x=l;x<=r;x++) if(abs((x-l)-(r-x))<=2) if(a[x]<=itv&&itv<b[x]){
work(l,x-1,len,itv);work(x+1,r,len,itv);
for(int i=1;i<=len;i++){
dp[id[l][r]][i]=(dp[id[l][r]][i]+1ll*
dp[id[l][x-1]][i]*dp[id[x+1][r]][i-1])%MOD;
}
}
for(int k=1;k<=len;k++) add(dp[id[l][r]][k],dp[id[l][r]][k-1]);
}
int iv[MAXN+5],ifac[MAXN+5];
void init_fac(int n){
for(int i=(ifac[0]=ifac[1]=1)+1;i<=n;i++) ifac[i]=1ll*ifac[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=n;i++) ifac[i]=1ll*ifac[i-1]*ifac[i]%MOD;
}
void getval(int l,int r){
if(r-l+1<=n+1){
for(int i=1;i<=itvl_n;i++) dp[i][0]=dp[i][r-l+1];
return;
} for(int i=1;i<=n+1;i++) iv[i]=qpow(r-(l+i-1),MOD-2);
for(int i=1;i<=itvl_n;i++){
if(itvls[i].fi>itvls[i].se) continue;
int len=itvls[i].se-itvls[i].fi+1,tot=1;
for(int j=1;j<=len+1;j++) tot=1ll*tot*(r-(l+j-1))%MOD;
dp[i][0]=0;
for(int j=1;j<=len+1;j++){
int mul=1ll*ifac[j-1]*ifac[len+1-j]%MOD*tot%MOD*iv[j]%MOD;
if((len+1-j)&1) mul=MOD-mul;
dp[i][0]=(dp[i][0]+1ll*mul*dp[i][j])%MOD;
}
}
}
int main(){
// freopen("robot.in","r",stdin);
// freopen("robot.out","w",stdout);
scanf("%d",&n);init_fac(n+3);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i],&b[i]);++b[i];
key[i*2-1]=a[i];key[i*2]=b[i];
} sort(key+1,key+(n<<1)+1);
for(int i=1;i<=n<<1;i++) if(key[i]^key[i-1])
uni[++num]=key[i];
for(int i=1;i<=n;i++){
a[i]=lower_bound(uni+1,uni+num+1,a[i])-uni;
b[i]=lower_bound(uni+1,uni+num+1,b[i])-uni;
} find_itvl(1,n);
for(int i=1;i<num;i++){
// printf("%d %d\n",uni[i],uni[i+1]-1);
memset(vis,0,sizeof(vis));
for(int l=1;l<=n;l++) for(int r=l-1;r<=n;r++)
if(id[l][r]) work(l,r,min(uni[i+1]-uni[i],n+1),i);
getval(uni[i],uni[i+1]-1);
// for(int j=1;j<=itvl_n;j++) printf("%d %d %d\n",i,j,dp[j][0]);
} printf("%d\n",dp[id[1][n]][0]);
return 0;
}
洛谷 P5469 - [NOI2019] 机器人(区间 dp+拉格朗日插值)的更多相关文章
- 洛谷P2470 [SCOI2007]压缩(区间dp)
题意 题目链接 Sol 神仙题Orz 考虑区间dp,如果我们只设\(f[l][r]\)表示\(s_{lr}\)被压缩的最小长度,而不去关心内部\(M\)分布的话,可能在转移的时候转移出非法状态 因此考 ...
- 洛谷P1018乘积最大——区间DP
题目:https://www.luogu.org/problemnew/show/P1018 区间DP+高精,注意初始化和转移的细节. 代码如下: #include<iostream> # ...
- 洛谷P1220关路灯——区间DP
题目:https://www.luogu.org/problemnew/show/P1220 区间DP. 代码如下: #include<iostream> #include<cstd ...
- 洛谷P1040 加分二叉树(区间dp)
P1040 加分二叉树 题目描述 设一个n个节点的二叉树tree的中序遍历为(1,2,3,…,n),其中数字1,2,3,…,n为节点编号.每个节点都有一个分数(均为正整数),记第i个节点的分数为di, ...
- 洛谷 P1080 石子合并 ( 区间DP )
题意 : 在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分.试设计出1个算法,计算出将N堆石子合并成1堆 ...
- 洛谷$P1864\ [NOI2009]$二叉查找树 区间$dp$
正解:区间$dp$ 解题报告: 传送门$QwQ$ 首先根据二叉查找树的定义可知,数据确定了,这棵树的中序遍历就已经改变了,唯一能改变的就是通过改变权值从而改变结点的深度. 发现这里权值的值没有意义,所 ...
- 洛谷P1063能量项链(区间dp)
题目描述: 给定一串序列x[],其中的每一个Xi看作看作一颗珠子,每个珠子包含两个参数,head和tail,前一颗的tail值是后一个的head值,珠子呈现环形(是一条项链),所以最后一颗的tail是 ...
- 洛谷 P1043 数字游戏 区间DP
题目描述 丁丁最近沉迷于一个数字游戏之中.这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易.游戏是这样的,在你面前有一圈整数(一共n个),你要按顺序将其分 ...
- 洛谷 P1220 关路灯 区间DP
题目描述 某一村庄在一条路线上安装了 n 盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少).老张就住在这条路中间某一路灯旁,他有一项工作就是每天早上天亮时一盏一盏地关掉这些路灯. 为了 ...
随机推荐
- SharkCTF2021 bybypass&baby_phpserialize题记
(国庆褪10天了 先水一篇) bybypass: payload:?anime_is_bae=hehellotherehoomanllotherehooman baby_phpserialize ro ...
- 【UE4】GAMES101 图形学作业1:mvp 模型、视图、投影变换
总览 到目前为止,我们已经学习了如何使用矩阵变换来排列二维或三维空间中的对象.所以现在是时候通过实现一些简单的变换矩阵来获得一些实际经验了.在接下来的三次作业中,我们将要求你去模拟一个基于CPU 的光 ...
- NOIP模拟83(多校16)
前言 CSP之后第一次模拟赛,感觉考的一般. 不得不吐槽多校联测 OJ 上的评测机是真的慢... T1 树上的数 解题思路 感觉自己思维有些固化了,一看题目就感觉是线段树. 考完之后才想起来这玩意直接 ...
- 攻防世界 杂项 11.simple_transfer
题目描述: 文件里有flag,找到它. 题目分析: 文件解压后是一个抓包流量分析文件,用 wireshark 打开后,用分组字节流搜索 flag,追踪 TCP 流,可以隐约看到有 file.pdf, ...
- C++实现红黑树
红黑树的应用: 利用key_value对,快速查找,O(logn) socket与客户端id之间,形成映射关系(socket, id) 内存分配管理 一整块内存,不断分配小块 每分配一次,就加入到红黑 ...
- Mac搭建以太坊私有链
记录过程与问题 一.安装 以go版本的ethereum进行安装 brew tap ethereum/ethereum brew install ethereum # 如果希望基于ethereum的de ...
- # Host xx.xxx.x.xxx found: line 1 /root/.ssh/known_hosts updated. Original contents retained as /root/.ssh/known_hosts.old
一直可以ssh登录远程服务器,突然不行了. 原因:远程服务器最近打过安全补丁,安全标识已经更新. 清理本机的安全密匙即可 解决办法: #ssh-keygen -R "需要远程服务器ip地址& ...
- 【mysql2】下载安装mysql5.7版|不再更新系列
一.下载MySQL 5.7 版 MySQL 5.7 版:官网下载地址 https://dev.mysql.com/downloads/windows/installer/5.7.html 下载的是50 ...
- GoLang设计模式13 - 观察者模式
观察者模式是一种行为型设计模式.这种模式允许一个实例(可以称为目标对象)发布各种事件(event)给其他实例(观察者).这些观察者会对目标对象进行订阅,这样每当目标对象发生变化时,观察者就会收到事件( ...
- 如何用LOTO示波器安全的测量高电压?
本文里我们把几十伏以上,超出对人体安全电压或者超出示波器量程的电压定为高电压,以220V市电举例讨论. 示波器上是有方便自测和探头补偿用的标准方波的,一般是1K Hz.我们的USB示波器上也有这个标准 ...