• 「NOI2019」机器人 (维护多项式dp值)


    「NOI2019」机器人 (维护多项式dp值)

    写的时候觉得这个题整个复杂度升天,代码升天,然后发现正解居然可以真的这样模拟。。。

    (而且自己拉格朗日不熟练所以就咕咕咕。。。)

    Part 35-50

    首先是一个简单的区间dp

    定义\(dp[l][r][Max]\)为区间\([l,r]\)中最大的数为\(Max\)的方案数

    转移上,类似构建笛卡尔树,枚举区间中最大的点\(k\)

    由于题目的限制,我们令\(Max_{lson}\leq Val_k<Max_{rson}\)

    这样,\(k\)这个点行走的区域就是\([l,k-1],[k+1,r]\),带入题目的限制即可

    复杂度\(O(n^3+n^2 Max)\)

    但是由于两边的访问区间极其接近二叉树,所以\(\text{dfs}\)之后可以发现,所需访问的节点完全达不到\(O(n^2)\),接近\(10n\)

    所以直接用dfs,状态前两维标号就可以了

    Part100

    考虑边界条件\(dp[i][i]\)

    可以发现

    \(dp[i][i][j]=\left\{ \begin{aligned} 0 && j<L_i \\ 1 && j\in [L_i,R_i] \\ 0 && j>R_i\end{aligned}\right.\)

    这是一个分段的0次多项式(??)

    转移

    \(dp[l][r][Max]\leftarrow dp[l][k-1][1..Max]\cdot dp[k+1][r][1..Max-1]\)

    我们知道任何一个\(n\)次多项式的前缀和都可以表示为一个\(n+1\)次多项式

    所以可以归纳得到\(dp[i][j][k]\)是一个关于\(k\)\(j-i\)次的分段多项式

    其中分段数显然是\(O(r-l)\)

    那么我们考虑每次合并两个前缀和,然后相乘得到\(dp[i][j][k]\),再自己进行累和

    如何将前缀和转化为多项式?分成\(n\)\(i^k\)前缀和,然后通过拉格朗日插值法得到

    不要问我复杂度是多少,我们都是有梦想的人。。

    #include<bits/stdc++.h>
    using namespace std;
    
    #define reg register
    typedef long long ll;
    #define rep(i,a,b) for(int i=a,i##end=b; i<=i##end;++i)
    #define drep(i,a,b) for(int i=a,i##end=b; i>=i##end;--i)
    
    #define Mod1(x) ((x>=P)&&(x-=P))
    #define Mod2(x) ((x<0)&&(x+=P))
    #define pb push_back
    template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
    template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }
    
    char IO;
    int rd(){
    	int s=0,f=0;
    	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    	do s=(s<<1)+(s<<3)+(IO^'0');
    	while(isdigit(IO=getchar()));
    	return f?-s:s;
    }
    
    const int N=310,P=1e9+7;
    
    bool be;
    
    int n;
    int L[N],R[N];
    
    typedef vector <int> Poly;
    // 多项式计算部分
    ll qpow(ll x,ll k=P-2) {
    	ll res=1;
    	for(;k;k>>=1,x=x*x%P) if(k&1) res=res*x%P;
    	return res;
    }
    Poly operator * (const Poly a,const Poly b) {
    	Poly res(a.size()+b.size()-1);
    	rep(i,0,a.size()-1) rep(j,0,b.size()-1) res[i+j]=(res[i+j]+1ll*a[i]*b[j])%P;
    	return res;
    }
    Poly operator + (Poly a,Poly b) {
    	if(a.size()<b.size()) swap(a,b);
    	rep(i,0,b.size()-1) a[i]+=b[i],Mod1(a[i]);
    	return a;
    }
    ll Calc(Poly a,int x){ 
    	ll res=0;
    	for(int i=0,t=1;i<(int)a.size();++i,t=1ll*t*x%P) res=(res+1ll*t*a[i])%P;
    	return res;
    }
    
    int a[N],b[N];
    Poly sik[N],T[N*100];
    // sik: sum i^k前缀和系数
    int h[N*100],hc;
    
    struct Node{ 
    	int L,R,S; Poly V;
    	bool operator < (const Node __) const { return L<__.L; }
    }Empty;
    vector <Node> dp[N][N]; // 存分段的dp函数
    
    Poly PrefixSum(Poly V) {
    	Poly res(V.size()+1);
    	rep(i,0,V.size()-1) if(V[i]) rep(j,0,sik[i].size()-1) res[j]=(res[j]+1ll*V[i]*sik[i][j])%P;
    	return res;
    } // 将多项式转化为前缀和
    
    Poly Que(vector <Node> &V,int x){ 
    	if(V.rbegin()->R<=x){ Poly temp; temp.pb(V.rbegin()->S); return temp;}
    	if(V[0].L>x) { Poly temp(1); return temp; }
    	Empty.L=x;
    	int p=upper_bound(V.begin(),V.end(),Empty)-V.begin()-1;
    	if(V[p].L<=x && V[p].R>x) return V[p].V;
    	else { Poly temp(1); return temp; }
    } // 找到x对应的多项式分段
    
    int C[N][N];
    // x -> x-1
    Poly Down(Poly x) {
    	rep(i,0,x.size()-1) {
    		rep(j,0,i-1) { 
    			if((i-j)&1) x[j]-=1ll*x[i]*C[i][j]%P,Mod2(x[j]);
    			else x[j]+=1ll*x[i]*C[i][j]%P,Mod1(x[j]);
    		}
    	}
    	return x;
    } // 由于右边是-1的前缀和,所以要把x-> x-1,直接二项式定理展开即可
    
    void dfs(int L,int R) {
    	if(dp[L][R].size()) return;
    	if(L==R+1) {
    		Poly tmp; tmp.pb(1);
    		dp[L][R].pb((Node){0,1,1,tmp});
    		return;
    	}
    	if(L==R) {
    		int l=::L[L],r=::R[L];
    		Poly tmp; tmp.pb(P-l+1); tmp.pb(1);
    		dp[L][R].pb((Node){l,r,r-l,tmp});
    		return;
    	}
    	int mid=(L+R)>>1;
    	hc=0;
    	rep(i,max(L,mid-2),min(R,mid+2)) if(abs((i-L)-(R-i))<=2) dfs(L,i-1),dfs(i+1,R);
    	rep(i,max(L,mid-2),min(R,mid+2)) if(abs((i-L)-(R-i))<=2) {
    		h[++hc]=::L[i],h[++hc]=::R[i];
    		for(Node p:dp[L][i-1]) {
    			h[++hc]=p.L;
    			h[++hc]=p.R;
    		}
    		for(Node p:dp[i+1][R]) {
    			h[++hc]=p.L+1;
    			h[++hc]=p.R+1;
    		}
    	}
    	sort(h+1,h+hc+1),hc=unique(h+1,h+hc+1)-h-1;
        // 预处理分段
    	rep(i,1,hc) T[i].clear();
    	rep(i,max(L,mid-2),min(R,mid+2)) 
            if(abs((i-L)-(R-i))<=2) 
                rep(j,1,hc-1) if(::L[i]<=h[j] && h[j]<::R[i]) 
                	T[j]=T[j]+Que(dp[L][i-1],h[j])*Down(Que(dp[i+1][R],h[j]-1));
        // 转移时相乘即可
    	int lst=0;
    	rep(i,1,hc-1) if(T[i].size()) {
    		T[i]=PrefixSum(T[i]);
    		T[i][0]-=Calc(T[i],h[i]-1),Mod2(T[i][0]);
    		T[i][0]+=lst,Mod1(T[i][0]);
    		lst=Calc(T[i],h[i+1]-1);
    		while(T[i].size() && *T[i].rbegin()==0) T[i].pop_back();
    		if(!T[i].size()) continue; // 小剪枝
    		dp[L][R].pb((Node){h[i],h[i+1],lst,T[i]});
            // 累成前缀和
    	}
    	if(!dp[L][R].size()) {
    		Poly temp(1);
    		dp[L][R].pb((Node){0,1,0,temp});
    	} // 如果出现dp值为空要特判
    	return;
    }
    
    
    Poly Lang(int n,int *a,int *b){
    	Poly res(n),c(n+1),tmp;
    	c[0]=1;
    	//  c = (x-a[i])
    	rep(i,0,n-1) {
    		drep(j,i+1,0) {
    			c[j]=(-1ll*c[j]*a[i]%P+(j?c[j-1]:0)+P)%P;
    		}
    	}
    	rep(i,0,n-1) {
    		ll t=1;
    		rep(j,0,n-1) if(i!=j) t=t*(a[i]-a[j])%P;
    		t=(qpow(t)*b[i]%P+P)%P;
    		tmp=c;
    		// c/(x-a[i])
    		drep(j,n-1,0) {
    			res[j]=(res[j]+t*tmp[j+1])%P;
    			tmp[j]=(tmp[j]+1ll*a[i]*tmp[j+1])%P;
    		}
    	}
    	return res;
    } // 拉格朗日插值
    
    int main(){
    	freopen("robot.in","r",stdin),freopen("robot.out","w",stdout);
    	n=rd();
    	rep(i,0,n+2) rep(j,C[i][0]=1,i) C[i][j]=(C[i-1][j-1]+C[i-1][j])%P; // 预处理组合数
    	rep(i,0,n+2){
    		ll s=0;
    		rep(j,1,i+1) {
    			s+=qpow(j,i),Mod1(s);
    			a[j]=j,b[j]=s;
    		}
    		sik[i]=Lang(i+2,a,b);
    	}
    	rep(i,1,n) L[i]=rd(),R[i]=rd()+1;
    	dfs(1,n);
    	printf("%d\n",dp[1][n].rbegin()->S);
    }
    
    
    
    
  • 相关阅读:
    安装高版本的docker
    Apache JMeter汉化手册
    安装python包
    Jmeter Cluster
    doc下设置永久环境变量的好方法
    jmeter非常好的博客收藏
    mysql-学习链接
    python 脚本
    常见python快捷键
    2015年心情随笔--周围太烦躁,我想静静
  • 原文地址:https://www.cnblogs.com/chasedeath/p/12809374.html
Copyright © 2020-2023  润新知