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; 
}

  

posted @ 2020-08-03 16:48  EM-LGH  阅读(229)  评论(0编辑  收藏  举报