LOJ#3157. 「NOI2019」机器人 DP+拉格朗日插值
NOI2019 两道插值可还行.
一个数不可能向右移动到超过后缀最大值的位置,也不可能向前移到前缀最大值之前的位置.
那么就考虑基于最大值的分治(DP)
令 $f[l][r][x]$ 表示当前区间为 $[l,r]$ 最大值为 $x$ 的方案数.
然后转移的话枚举 $k$ 为最大值出现的位置(有多个的话则是最后出现的位置).
那么就将问题分成两个子问题了,时间复杂度为 $O(nMW)$,其中 $M,W$ 分别为区间个数和值域.
$35$ pts 的暴力分就是 $M=n^2$,即每个区间都枚举,总复杂度是 $O(n^2W)$.
但是我们打表发现有用的区间 $M$ 最多为 $3000$,那么提前记忆化搜索的话复杂度就是 $O(10nW)$ 了.
考虑 $l=1,r=10^9$ 的点,由于每个位置的取值范围都是相同的,我们可以暴力求出最大值为 $1$ ~ $n$ 的点.
然后可以用容斥+组合来算.
还有一种能推广到正解的做法就是用拉格朗日插值法.
可以归纳,$f[l][r]$ 是一个不超过 $r-l$ 次的多项式.
考虑当 $l=r$ 时显然成立(就是一个常数),然后对 $f$ 求前缀和的话多项式的次数+1.
最后在合并的时候本质上是两个多项式相乘,次数为 $len-1$.
对于每个位置取值不同的点就将所有点排序,然后以相邻两个点为值域仿照上面的做法去做 DP.
那么假设当前的区间为 $[a,b]$ 那么就将 $[1,a)$ 的部分当作常数项处理.
这里有两个细节要注意:
1. 加入区间的时候要加入 $[a,b)$ 因为如果加入 $[a,a]$ 的话可能会处理不到 $[a,a]$ 这种情况.
2. 处理到 $[a_{i-1},a_{i}]$ 的时候要先处理 $[a_{i-1},a_{i}-1]$ 然后再处理 $[a_{i},a_{i}]$,因为端点处可能存在交界.
code:
#include <cstdio>
#include <ctime>
#include <vector>
#include <cmath>
#include <cstring>
#include <algorithm>
#define N 307
#define M 3005
#define ll long long
#define mod 1000000007
#define setIO(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
using namespace std;
int qpow(int x,int y) {
int tmp=1;
for(;y;y>>=1,x=(ll)x*x%mod) {
if(y&1) tmp=(ll)tmp*x%mod;
}
return tmp;
}
inline int get_inv(int x) {
return qpow(x,mod-2);
}
inline int ADD(int x,int y) {
return x+y>=mod?x+y-mod:x+y;
}
inline int DEC(int x,int y) {
return x-y<0?x-y+mod:x-y;
}
namespace Lagrange {
int x[N],y[N],fac[N],inv[N],pre[N],suf[N];
void prep() {
fac[0]=1;
for(int i=1;i<N;++i) fac[i]=(ll)fac[i-1]*i%mod;
inv[1]=1;
for(int i=2;i<N;++i) {
inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
}
inv[0]=1;
for(int i=1;i<N;++i) {
inv[i]=(ll)inv[i-1]*inv[i]%mod;
}
}
void init(int v,int kth) {
for(int i=0;i<=kth;++i) x[i]=i;
pre[0]=suf[kth+1]=1;
for(int i=1;i<=kth;++i) {
pre[i]=(ll)(v-x[i-1]+mod)*pre[i-1]%mod;
}
for(int i=kth;i>=1;--i) {
suf[i]=(ll)(v-x[i]+mod)*suf[i+1]%mod;
}
}
int solve(int v,int kth) {
int an=0;
for(int i=0;i<=kth;++i) {
int up=1,dn=1;
dn=(ll)inv[i]*inv[kth-i]%mod;
if((kth-i)&1) dn=(ll)dn*(mod-1)%mod;
up=(ll)pre[i]*suf[i+1]%mod;
an=ADD(an,(ll)y[i]*up%mod*dn%mod);
}
return an;
}
};
int n;
bool vis[M];
int a[N],b[N],e[M],lim,pr;
int id[N][N],dp[M][N],len[M],tot,cnt;
struct data {
int l,r;
data(int l=0,int r=0):l(l),r(r){}
}arr[M];
void dfs(int l,int r) {
if(l>r||id[l][r]) return;
id[l][r]=++cnt;
arr[cnt]=data(l,r);
len[cnt]=r-l+1;
if(l==r) return;
int mid=(l+r)>>1,len=r-l+1;
if(len&1) {
dfs(l,mid-2),dfs(l,mid-1),dfs(l,mid);
dfs(mid+2,r),dfs(mid+1,r),dfs(mid,r);
}
else {
dfs(l,mid-1),dfs(l,mid);
dfs(mid+2,r),dfs(mid+1,r);
}
}
void get(int l,int r,int x,int now);
void solve(int l,int r) {
int now=id[l][r];
if(vis[now]||l>r) return;
vis[now]=1;
for(int i=1;i<=lim;++i) {
dp[now][i]=0;
}
if(l==r) {
for(int i=1;i<=lim;++i) {
dp[now][i]=dp[now][i-1]+(i+pr>=a[l]&&i+pr<=b[l]);
if(dp[now][i]>=mod) dp[now][i]-=mod;
}
return;
}
int mid=(l+r)>>1,len=(r-l+1);
if(len&1) {
get(l,r,mid-1,now);
get(l,r,mid,now);
get(l,r,mid+1,now);
}
else {
get(l,r,mid,now);
get(l,r,mid+1,now);
}
for(int i=1;i<=lim;++i) {
dp[now][i]=ADD(dp[now][i],dp[now][i-1]);
}
}
void get(int l,int r,int x,int now) {
if(x<l||x>r) return;
solve(l,x-1),solve(x+1,r);
int u=id[l][x-1],v=id[x+1][r];
for(int i=1;i<=lim;++i) {
if(i+pr>=a[x]&&i+pr<=b[x])
dp[now][i]=ADD(dp[now][i],(ll)dp[u][i]*dp[v][i-1]%mod);
}
}
int main() {
// setIO("robot");
scanf("%d",&n);
for(int i=1;i<=n;++i) {
scanf("%d%d",&a[i],&b[i]);
e[++tot]=a[i],e[++tot]=b[i]+1;
}
Lagrange::prep();
sort(e+1,e+1+tot);
dfs(1,n);
int x,y,z;
for(int i=0;i<=n+3;++i) {
dp[0][i]=1;
}
for(int i=1;i<=tot;++i) {
if(e[i]!=e[i-1]) {
pr=e[i-1];
lim=min(e[i]-e[i-1]-1,n+3);
memset(vis,false,sizeof(vis));
solve(1,n);
if(lim==e[i]-e[i-1]-1) {
for(int j=1;j<=cnt;++j)
dp[j][0]=dp[j][lim];
}
else {
for(int j=1;j<=cnt;++j) {
for(int p=0;p<=len[j];++p) {
Lagrange::y[p]=dp[j][p];
}
Lagrange::init(e[i]-e[i-1]-1,len[j]);
dp[j][0]=Lagrange::solve(e[i]-e[i-1]-1,len[j]);
}
}
lim=1,pr=e[i]-1;
memset(vis,false,sizeof(vis));
solve(1,n);
for(int j=1;j<=cnt;++j) {
dp[j][0]=dp[j][1];
}
}
}
printf("%d\n",dp[id[1][n]][0]);
return 0;
}

浙公网安备 33010602011771号