【NOI2019】机器人(区间DP)(记忆化搜索)(下降幂多项式)

传送门


题解:

首先考虑一个区间DP,枚举区间中的最大值最右边的位置,左边所有点不大于这个位置,右边所有点严格小于这个位置。

容易发现这个位置只能是中间几个地方。

d p [ l ] [ r ] [ x ] dp[l][r][x] dp[l][r][x] 表示考虑区间 [ l , r ] [l,r] [l,r],其中最大值为 x x x 的时候的合法方案数。

容易注意到有转移:

d p [ l ] [ r ] [ x ] = ∑ ∣ ( r − i ) − ( i − l ) ∣ ≤ 2 ( ∑ j = 1 x d p [ l ] [ i − 1 ] [ j ] ) ( ∑ j = 1 x − 1 d p [ i + 1 ] [ r ] [ j ] ) dp[l][r][x]=\sum_{|(r-i)-(i-l)|\leq 2}(\sum_{j=1}^xdp[l][i-1][j])(\sum_{j=1}^{x-1}dp[i+1][r][j]) dp[l][r][x]=(ri)(il)2(j=1xdp[l][i1][j])(j=1x1dp[i+1][r][j])

转移里面有一个前缀和,考虑在这上面进行优化。

容易发现由于 x x x 的取值范围非常大,我们并不能显式维护前缀和。

可以考虑利用多项式,如果直接用生成函数的话会发现求前缀和的时候要套上一个 1 1 − x \frac{1}{1-x} 1x1 ,感觉很烦。

考虑利用点值来做,设 F l , r ( x ) = d p [ l ] [ r ] [ x ] F_{l,r}(x)=dp[l][r][x] Fl,r(x)=dp[l][r][x],容易发现其实后面就变成了点值前缀和,也就是说我们需要支持快速求一个多项式点值前缀和的多项式,说人话就是求 G ( x ) = ∑ i = 1 x F ( i ) G(x)=\sum_{i=1}^xF(i) G(x)=i=1xF(i)

G ( x ) = G ( x − 1 ) + F ( x ) G(x)=G(x-1)+F(x) G(x)=G(x1)+F(x),有一个点值的转移,可以拉格朗日插值,也可以用下降幂来做。

注意到 ( x − 1 ) i ‾ = x i ‾ − i ( x − 1 ) i − 1 ‾ (x-1)^{\underline i}=x^{\underline i}-i(x-1)^{\underline{i-1}} (x1)i=xii(x1)i1,可以得到 G ( x − 1 ) G(x-1) G(x1) 的各项系数,进而得到 G ( x ) G(x) G(x) 的各项系数。

转移过程中需要将 [ A i , B i ] [A_i,B_i] [Ai,Bi] 外的点值变为 0,显式维护分段函数即可。

剩下的就是大力写了。复杂度 O ( 能 过 ) O(能过) O(),不知道有没有什么靠谱的多项式描述。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int mod=1e9+7;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}

typedef std::vector<int> Poly;
typedef std::pair<Poly,int> nd;
typedef std::vector<nd> Func;
#define fi first
#define se second

cs int N=3e2+7;

int inv[N];

Poly& operator+=(Poly &a,cs Poly &b){
	if(a.size()<b.size())a.resize(b.size());
	for(int re i=0;i<(int)b.size();++i)Inc(a[i],b[i]);
	return a;
}Poly operator+(Poly a,cs Poly &b){return a+=b;}

Poly operator*(cs Poly &a,cs Poly &b){
	int al=a.size(),bl=b.size();
	Poly c(al+bl-1);static int s[N];
	for(int re i=0;i<bl;++i)s[i]=b[i];
	for(int re i=0;i<al;++i){
		for(int re j=0;j<bl;++j)Inc(c[i+j],mul(a[i],s[j]));
		for(int re j=1;j<bl;++j)Inc(s[j-1],mul(s[j],j));
	}return c;
}

Poly trans(Poly a){
	a.resize(a.size()+1,0);
	for(int re i=a.size()-1;i;--i)
		a[i]=mul(a[i-1],inv[i]);
	a[0]=0;return a;
}

int calc(cs Poly &a,int x){
	int res=0,mult=1;
	for(int re i=0;i<(int)a.size();++i)
		Inc(res,mul(a[i],mult)),Mul(mult,dec(x,i));
	return res;
}

Func operator+(cs Func &a,cs Func &b){
	Func c;size_t i=0,j=0;int nx=0;
	while(true){
		c.push_back({a[i].fi+b[j].fi,nx});
		if(i+1==a.size()&&j+1==b.size())return c;
		if(i+1<a.size()&&(j+1==b.size()||a[i+1].se<b[j+1].se))
			nx=a[++i].se;
		else nx=b[++j].se;
		while(i+1<a.size()&&a[i+1].se<=nx)++i;
		while(j+1<b.size()&&b[j+1].se<=nx)++j;
	}
}

Func operator*(cs Func &a,cs Func &b){
	Func c;size_t i=0,j=0;int nx=0;
	while(true){
		c.push_back({a[i].fi*b[j].fi,nx});
		if(i+1==a.size()&&j+1==b.size())return c;
		if(i+1<a.size()&&(j+1==b.size()||a[i+1].se<b[j+1].se))
			nx=a[++i].se;
		else nx=b[++j].se;
		while(i+1<a.size()&&a[i+1].se<=nx)++i;
		while(j+1<b.size()&&b[j+1].se<=nx)++j;
	}
}

Func get(cs Func &a,int l,int r){
	Func b;b.push_back({Poly(1),0});
	for(size_t i=0;i<a.size();++i)
		if(a[i].se<=r&&(i+1==a.size()||a[i+1].se>l))
			b.push_back({a[i].fi,std::max(a[i].se,l)});
	b.push_back({Poly(1),r+1});
	return b;
}

Func trans(cs Func &a){
	Func b;
	for(size_t i=0;i<a.size();++i){
		b.push_back({trans(a[i].fi),a[i].se});
		if(i)b[i].fi[0]=dec(calc(b[i-1].fi,a[i].se),calc(b[i].fi,a[i].se));
	}return b;
}

int n;
int A[N],B[N];
int id[N][N],tot;
Func f[N<<4];

void dfs(int l,int r){
	if(id[l][r])return;
	int nw=id[l][r]=++tot;
	f[nw].push_back({Poly(1),0});
	if(l>r){f[nw][0].fi[0]=1;return;}
	for(int re i=l;i<=r;++i)
	if(abs(r+l-i-i)<=2){
		dfs(l,i-1);dfs(i+1,r);
		Func L=f[id[l][i-1]],R=f[id[i+1][r]];
		if(l<i)L=L+trans(L);if(i<r)R=trans(R);
		f[nw]=f[nw]+get(L*R,A[i],B[i]);
	}
}

void Main(){
	scanf("%d",&n);inv[0]=inv[1]=1;
	for(int re i=2;i<N;++i)
		inv[i]=mul(inv[mod%i],mod-mod/i);
	for(int re i=1;i<=n;++i)
		scanf("%d%d",A+i,B+i);
	dfs(1,n);
	cout<<trans(f[1]).back().fi[0]<<"\n";
}

inline void file(){
#ifdef zxyoi
	freopen("robot.in","r",stdin);
#else
	freopen("robot.in","r",stdin);
	freopen("robot.out","w",stdout);
#endif
}signed main(){file();Main();return 0;}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值