注意到只有位置最靠右的最大值可以走遍整个序列,并且其余任何点都不能跨过这个位置,因此可以区间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;
}