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

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章