• 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
    ",dp[id[1][n]][0]);  
    	return 0; 
    }
    

      

  • 相关阅读:
    echarts圆套圆
    两个对象深度比较,借鉴,记录
    js异步加载的方式
    elementUI使用el-card高度自适应
    如何在页面上实现一个圆形的可点击区域
    清除浮动
    水平垂直居中的几种方式
    BFC原理
    正则表达式
    Vue项目中难点问题
  • 原文地址:https://www.cnblogs.com/guangheli/p/13427228.html
Copyright © 2020-2023  润新知