zoukankan      html  css  js  c++  java
  • 「NOI2019」机器人

    注意到只有位置最靠右的最大值可以走遍整个序列,并且其余任何点都不能跨过这个位置,因此可以区间DP。(我连这个都想不到啊喂,考场直接爆零)
    (f_{l,r,k})表示区间([l,r])最大值为(k)的方案数,令(g_{l,r,k}=sum_{i leq k} f_{l,r,i})(f_{l,r,k}=sum g_{l,t-1,k} imes g_{t+1,r,k-1})
    直接记忆化搜索发现其实用到的区间并不多,约为3000个,可以获得50分
    考虑(A=1,B=10^9),令(f_{l,r}(k)=f_{l,r,k})发现函数(f)总为次数不超过(r-l+1)的多项式,可以记录该多项式的点值表示进行转移,然后用拉格朗日插值还原。
    现在(A,B)没有限制,我们可以对值域分段为([L,R])([L,R])不会和任意一个柱子的区间有交叉,于是可以求出来定义域在([L,R])内的多项式函数(f)(g)),(g(L-1))已经求好,这次我们直接令(g(L-1))为一个常数,然后就和上面的转移很类似了。
    这种拿点值转移的方法时间复杂度约为(O(3000 imes n^2)),可以获得(95)分。

    //https://www.cnblogs.com/Soulist/p/13778475.html
    //https://blog.csdn.net/qq_39972971/article/details/97910617
    #include<bits/stdc++.h>
    using namespace std;
    #define fp(i,l,r) for(register int (i)=(l);(i)<=(r);++(i))
    #define fd(i,l,r) for(register int (i)=(l);(i)>=(r);--(i))
    #define fe(i,u) for(register int (i)=front[(u)];(i);(i)=e[(i)].next)
    #define mem(a) memset((a),0,sizeof (a))
    #define O(x) cerr<<#x<<':'<<x<<endl
    #define int long long
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    void wr(int x){
    	if(x<0)putchar('-'),x=-x;
    	if(x>=10)wr(x/10);
    	putchar('0'+x%10);
    }
    const int MAXN=305,MAXM=3e3+5,mod=1e9+7;
    inline void tmod(int &x){x%=mod;}
    inline void rmod(int &x){x+=x>>31&mod;}
    inline int qpow(int a,int b){
    	int res=1;a%=mod;
    	for(;b;b>>=1,tmod(a*=a))
    	if(b&1)tmod(res*=a);
    	return res;
    }
    inline int ginv(int x){return qpow(x,mod-2);}
    inline int norm(int x){return (x%mod+mod)%mod;}
    int n,sta[MAXN*2],fac[MAXN],ifac[MAXN],top,dp[MAXM][MAXN],id[MAXN][MAXN],cnt;
    bool vis[MAXM];
    int L,R,s1[MAXN],s2[MAXN],A[MAXN],B[MAXN];
    //dp为函数g的点值表示,假如当前区间为[L,R],每次记录[L-1,L+n-1]的点值
    inline int getval(int a[],int x){
    	if(x<=L+n-1)return a[x-L+1];
    	tmod(s1[0]=x-(L-1));s2[n+1]=1;
    	fp(i,1,n)tmod(s1[i]=s1[i-1]*(x-(L-1+i)));
    	fd(i,n,0)tmod(s2[i]=s2[i+1]*(x-(L-1+i)));
    	int ans=0;
    	fp(i,0,n)tmod(ans+=a[i]*(i?s1[i-1]:1)%mod*s2[i+1]%mod*ifac[i]%mod*((n-i&1)?-1:1)*ifac[n-i]);
    	return norm(ans);
    }
    void dfs(int l,int r){
    	if(l>r)return;
    	if(id[l][r]&&vis[id[l][r]])return;
    	if(!id[l][r])id[l][r]=++cnt;int o=id[l][r];vis[o]=1;
    	fp(j,1,n)dp[o][j]=0;
    	int lx=max(l,(r+l-1)/2),rx=min(r,(r+l+2)/2) ; 
    	fp(i,lx,rx){
    		dfs(l,i-1);dfs(i+1,r);
    		int ls=id[l][i-1],rs=id[i+1][r];
    		if(L>=A[i]&&R<=B[i])fp(j,1,n)tmod(dp[o][j]+=dp[ls][j]*dp[rs][j-1]);
    	}
    	fp(j,1,n)rmod(dp[o][j]+=dp[o][j-1]-mod);
    }
    main(){
    	fac[0]=1;fp(i,1,MAXN-1)tmod(fac[i]=fac[i-1]*i);ifac[MAXN-1]=ginv(fac[MAXN-1]);
    	fd(i,MAXN-2,0)tmod(ifac[i]=ifac[i+1]*(i+1));
    	n=read();
    	fp(i,1,n){
    		A[i]=read();B[i]=read();sta[++top]=A[i];sta[++top]=B[i]+1;
    	}
    	sort(sta+1,sta+1+top);top=unique(sta+1,sta+1+top)-sta-1;
    	fp(i,0,n)dp[0][i]=1;
    	fp(kk,1,top-1){
    		L=sta[kk],R=sta[kk+1]-1;
    		dfs(1,n);
    		fp(i,1,cnt)vis[i]=0,dp[i][0]=getval(dp[i],R);
    	}
    	printf("%lld
    ",dp[id[1][n]][0]);
    	return 0;
    }
    

    关于下降幂多项式:https://blog.csdn.net/qq_35950004/article/details/95517723
    由于我们区间的长度和也不会特别大,而上面做法即使是比较小的区间也得存(n)个点值,比较浪费(好像可以优化,但我太菜了不会。。)
    而下降幂多项式求前缀和(离散积分)是很优秀的,(sum_{i=0}^{x-1} i^{underline n} = frac{x^{underline {n+1}}}{n+1})(注意这么做是从(0)积到(x-1)!)
    计算下降幂多项式乘法的方法大概是把他乘(e^x)可以得到点值多项式,然后可以乘(e^{-x})变回来,注意点值相乘的过程中要多乘一个阶乘!
    (g(k-1))时需要用到((x-1)^{underline n}=x^{underline n}-n(x-1)^{underline {n-1}})
    实现较好可以获得(100)

    //https://www.cnblogs.com/Soulist/p/13778475.html
    //https://blog.csdn.net/qq_39972971/article/details/97910617
    #include<bits/stdc++.h>
    using namespace std;
    #define fp(i,l,r) for(register int (i)=(l);(i)<=(r);++(i))
    #define fd(i,l,r) for(register int (i)=(l);(i)>=(r);--(i))
    #define fe(i,u) for(register int (i)=front[(u)];(i);(i)=e[(i)].next)
    #define mem(a) memset((a),0,sizeof (a))
    #define O(x) cerr<<#x<<':'<<x<<endl
    #define int long long
    #define poly vector<int>
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    void wr(int x){
    	if(x<0)putchar('-'),x=-x;
    	if(x>=10)wr(x/10);
    	putchar('0'+x%10);
    }
    const int MAXN=605,MAXM=3e3+5,mod=1e9+7;//2 times here
    inline void tmod(int &x){x%=mod;}
    inline void rmod(int &x){x+=x>>31&mod;}
    inline int qpow(int a,int b){
    	int res=1;a%=mod;
    	for(;b;b>>=1,tmod(a*=a))
    	if(b&1)tmod(res*=a);
    	return res;
    }
    inline int ginv(int x){return qpow(x,mod-2);}
    inline int norm(int x){return (x%mod+mod)%mod;}
    int n,sta[MAXN],fac[MAXN],ifac[MAXN],top,id[MAXN][MAXN],cnt;
    bool vis[MAXM];
    int L,R,ex1[MAXN],ex2[MAXN],A[MAXN],B[MAXN],inv[MAXN],g[MAXM];
    poly dp[MAXM];
    inline void fdt(poly &a,const int ex[],int n){
    	a.resize(n+1);
    	fd(i,n,0){
    		tmod(a[i]*=ex[0]);
    		fd(j,i-1,0)tmod(a[i]+=a[j]*ex[i-j]);
    	}
    }
    inline poly operator*(poly a,poly b){
    	int t=(int)a.size()+(int)b.size()-2;
    	fdt(a,ex1,t);fdt(b,ex1,t);
    	fp(i,0,t)tmod(a[i]*=b[i]*fac[i]%mod);
    	fdt(a,ex2,t);while(!a.empty()&&!a.back())a.pop_back();
    	return a;
    }
    inline poly upset(poly a){//g(k)->g(k+1)
    	fp(i,1,(int)a.size()-1)tmod(a[i-1]+=a[i]*i);
    	return a;
    }
    inline poly downset(poly a){//g(k)->g(k-1)
    	fd(i,(int)a.size()-1,1)rmod(a[i-1]-=a[i]*i%mod);
    	return a;
    }
    inline void operator+=(poly &a,const poly &b){
    	a.resize(max(a.size(),b.size()));
    	fd(i,(int)b.size()-1,0)rmod(a[i]+=b[i]-mod);
    }
    inline int getval(const poly &a,int x){
    	int t=1,res=0;
    	fp(i,0,(int)a.size()-1)tmod(res+=a[i]*t),tmod(t*=x-i);
    	return res;
    }
    void dfs(int l,int r){
    	if(l>r)return;
    	if(id[l][r]&&vis[id[l][r]])return;
    	if(!id[l][r])id[l][r]=++cnt;int o=id[l][r];vis[o]=1;
    	int lx=max(l,(r+l-1)/2),rx=min(r,(r+l+2)/2);
    	fp(i,lx,rx){
    		dfs(l,i-1);dfs(i+1,r);poly tt=dp[o];
    		if(L>=A[i]&&R<=B[i])dp[o]+=dp[id[l][i-1]]*downset(dp[id[i+1][r]]);
    	}
    	dp[o].resize(dp[o].size()+1);int t=dp[o][0];
    	fd(i,(int)dp[o].size()-1,1)tmod(dp[o][i]=dp[o][i-1]*inv[i]);dp[o][0]=0;//这时dp(x)实际上是sum_{i=0}^{x-1} f(x)
    	dp[o]=upset(dp[o]);
    	tmod(dp[o][0]+=g[o]+mod-t);//-t是因为f(0)加了两次
    }
    main(){
    	fac[0]=1;fp(i,1,MAXN-1)tmod(fac[i]=fac[i-1]*i),inv[i]=ginv(i);ifac[MAXN-1]=ginv(fac[MAXN-1]);
    	fd(i,MAXN-2,0)tmod(ifac[i]=ifac[i+1]*(i+1));
    	fp(i,0,MAXN-1)ex1[i]=ifac[i],ex2[i]=i&1?mod-ifac[i]:ifac[i];
    	n=read();
    	fp(i,1,n){
    		A[i]=read();B[i]=read();sta[++top]=A[i];sta[++top]=B[i]+1;
    	}
    	sort(sta+1,sta+1+top);top=unique(sta+1,sta+1+top)-sta-1;
    	dp[0]={1};
    	fp(kk,1,top-1){
    		L=sta[kk],R=sta[kk+1]-1;
    		dfs(1,n);
    		if(kk!=top-1)fp(i,1,cnt)vis[i]=0,g[i]=getval(dp[i],R-L+1),dp[i].clear();
    		else printf("%lld
    ",getval(dp[id[1][n]],R-L+1));
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    单模光纤与多模光纤的区别
    普通的单模光纤和单模光纤跳线是否可以达到万兆速度?
    原创:在局域网中,如何知道某个IP被占用 show arp
    如何知道局域网内哪些ip被占用----工具法Free IP Scanner
    如何知道局域网内哪些ip被占用
    科普知识普及
    电脑通电自动开机
    2.4G还是5G?带你选择最正确的路由器
    光纤收发器指示灯介绍
    100BASE-TX、100Base-FX等含义
  • 原文地址:https://www.cnblogs.com/WinterSpell/p/14279085.html
Copyright © 2011-2022 走看看