题意:有个长度为 n n n的序列 a a a, a i ∈ [ L i , R i ] a_i\in [L_i,R_i] ai∈[Li,Ri]。从一个位置 s s s可以往左直到 ≥ a s \geq a_s ≥as,往右直到 > a s > a_s >as。求有多少种可能的序列满足从任意位置向左或向右的最大步数的差的绝对值不超过 2 2 2。
n ≤ 300 , R i ≤ 1 0 9 n\leq 300,R_i\leq 10^9 n≤300,Ri≤109
National Olympics in Interpolation
先对于一个给定序列判断是否合法
考虑最右边的最大值,它可以到序列的任意位置,所以它一定在序列正中间的 O ( 1 ) O(1) O(1)个位置。
左边的点都到不了右边,右边的点都到不了左边,所以可以分开考虑。
现在考虑计数。设 d p ( l , r , x ) dp(l,r,x) dp(l,r,x)表示 [ l , r ] [l,r] [l,r]这个区间最大值恰好为 x x x且满足题目中的要求的方案数
d p ( l , r , x ) = ∑ ∣ l + r − 2 k ∣ ≤ 2 [ ∑ i ≤ x d p ( l , k − 1 , i ) ] [ ∑ i < x d p ( k + 1 , r , i ) ] dp(l,r,x)=\sum _{|l+r-2k|\leq2}[\sum _{i\leq x}dp(l,k-1,i)][\sum_{i<x}dp(k+1,r,i)] dp(l,r,x)=∣l+r−2k∣≤2∑[i≤x∑dp(l,k−1,i)][i<x∑dp(k+1,r,i)]
根据套路,大胆猜想:
dp(l,r,x)是关于 x 的 r − l 次多项式 \text{dp(l,r,x)是关于$x$的$r-l$次多项式} dp(l,r,x)是关于x的r−l次多项式
证明很套路,略
先不管值域的限制,我们现在需要维护多项式求点值前缀和和多项式乘法
拉格朗日是个不错的方法,但是被凉心出题人卡了
这里需要用下降幂多项式的黑科技
首先有个显而易见的式子:
x i ‾ − ( x − 1 ) i ‾ = i ( x − 1 ) i − 1 ‾ x^{\underline i}-(x-1)^{\underline i}=i(x-1)^{\underline {i-1}} xi−(x−1)i=i(x−1)i−1
有什么用呢?
考虑一个下降幂多项式
f ( x ) = ∑ i a i x i ‾ f(x)=\sum_{i}a_ix^{\underline i} f(x)=i∑aixi
我们想用一些办法快速求它的前缀和。因为一些奇怪的原因,这里我们只加到 x − 1 x-1 x−1,即不包括这个位置本身
g ( x ) = ∑ i = 1 x − 1 f ( i ) = ∑ i = 1 x − 1 ∑ j a j i j ‾ g(x)=\sum_{i=1}^{x-1}f(i)\\=\sum_{i=1}^{x-1}\sum_{j}a_ji^{\underline j} g(x)=i=1∑x−1f(i)=i=1∑x−1j∑ajij
= ∑ j a j ∑ i = 1 x − 1 i j ‾ =\sum_j a_j\sum_{i=1}^{x-1} i^{\underline j} =j∑aji=1∑x−1ij
对于一个已知的 i i i,考虑这个怎么求:
∑ x = 1 n x i ‾ \sum_{x=1}^nx^{\underline i} x=1∑nxi
用上面的式子裂下项
∑ x = 1 n ( x + 1 ) i + 1 ‾ − x i + 1 ‾ i + 1 \sum_{x=1}^n\frac{(x+1)^{\underline{i+1}}-x^{\underline {i+1}}}{i+1} x=1∑ni+1(x+1)i+1−xi+1
= ( n + 1 ) i + 1 ‾ i + 1 =\frac{(n+1)^{^{\underline {i+1}}}}{i+1} =i+1(n+1)i+1
所以
g ( x ) = ∑ i a i x i + 1 ‾ i + 1 g(x)=\sum_i a_i\frac{x^{\underline {i+1}}}{i+1} g(x)=i∑aii+1xi+1
形式上和连续多项式的积分一样,不知道是不是巧合
再加上原来的多项式就得到了前缀和
对于两个下降幂多项式 f ( x ) , g ( x ) f(x),g(x) f(x),g(x)的乘法,可以从低到高枚举 f ( x ) f(x) f(x)的每一项,对于一个 x i ‾ x^{\underline i} xi,与右边的一个 ( x − i ) j ‾ (x-i)^{\underline j} (x−i)j相乘可以得到 x i + j ‾ x^{\underline {i+j}} xi+j。每枚举一个 i i i就再用上面的式子 O ( n ) O(n) O(n)地把 g ( x ) g(x) g(x)变成 g ( x − 1 ) g(x-1) g(x−1),然后暴力算卷积即可。
带上 L i , R i L_i,R_i Li,Ri的话就把 [ 1 , L i − 1 ] [1,L_i-1] [1,Li−1]和 [ R i + 1 , + ∞ ] [R_i+1,+\infin] [Ri+1,+∞]的部分置为 0 0 0,直接大力维护分段函数即可
复杂度是什么?可以吃吗?
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
#include <utility>
#define MAXN 350
using namespace std;
const int MOD=1e9+7;
typedef long long ll;
inline int qpow(int a,int p)
{
int ans=1;
while (p)
{
if (p&1) ans=(ll)ans*a%MOD;
a=(ll)a*a%MOD,p>>=1;
}
return ans;
}
int inv[MAXN];
inline int add(const int& x,const int& y){return x+y>=MOD? x+y-MOD:x+y;}
inline int dec(const int& x,const int& y){return x<y? x-y+MOD:x-y;}
typedef vector<int> poly;
typedef pair<poly,int> seg;
typedef vector<seg> func;
#define fir first
#define sec second
#define mp make_pair
inline int f(const poly& a,const int& x)
{
int ans=0;
for (int i=0,m=1;i<(int)a.size();m=(ll)m*(x-i)%MOD,++i)
ans=(ans+(ll)a[i]*m)%MOD;
return ans;
}
inline poly operator +(poly a,const poly& b)
{
a.resize(max(a.size(),b.size()));
for (int i=0;i<(int)b.size();i++) a[i]=add(a[i],b[i]);
return a;
}
inline poly operator *(const poly& a,poly b)
{
poly c(a.size()+b.size()-1);
for (int i=0;i<(int)a.size();i++)
{
for (int j=0;j<(int)b.size();j++) c[i+j]=(c[i+j]+(ll)a[i]*b[j])%MOD;
for (int j=1;j<(int)b.size();j++) b[j-1]=(b[j-1]+(ll)b[j]*j)%MOD;
}
return c;
}
inline poly integ(poly a)
{
a.push_back(0);
for (int i=a.size()-1;i>0;i--) a[i]=(ll)a[i-1]*inv[i]%MOD;
a[0]=0;
return a;
}
inline func operator +(func a,func b)
{
func c;
int i=0,j=0,pos=0;
while (true)
{
c.push_back(mp(a[i].fir+b[j].fir,pos));
if (i==(int)a.size()-1&&j==(int)b.size()-1) break;
if (i<(int)a.size()-1&&(j==(int)b.size()-1||a[i+1].sec<b[j+1].sec)) pos=a[++i].sec;
else pos=b[++j].sec;
if (i<(int)a.size()-1&&a[i+1].sec<=pos) ++i;
if (j<(int)b.size()-1&&b[j+1].sec<=pos) ++j;
}
return c;
}
inline func operator *(func a,func b)
{
func c;
int i=0,j=0,pos=0;
while (true)
{
c.push_back(mp(a[i].fir*b[j].fir,pos));
if (i==(int)a.size()-1&&j==(int)b.size()-1) break;
if (i<(int)a.size()-1&&(j==(int)b.size()-1||a[i+1].sec<b[j+1].sec)) pos=a[++i].sec;
else pos=b[++j].sec;
if (i<(int)a.size()-1&&a[i+1].sec<=pos) ++i;
if (j<(int)b.size()-1&&b[j+1].sec<=pos) ++j;
}
return c;
}
inline func integ(func a)
{
for (int i=0;i<(int)a.size();i++)
{
a[i].fir=integ(a[i].fir);
if (i) a[i].fir[0]=dec(f(a[i-1].fir,a[i].sec),f(a[i].fir,a[i].sec));
}
return a;
}
inline func cut(const func& a,const int& l,const int& r)
{
func b;
b.push_back(mp(poly(1),0));
for (int i=0;i<(int)a.size();i++)
if (a[i].sec<=r&&(i==(int)a.size()-1||l<a[i+1].sec))
b.push_back(mp(a[i].fir,max(a[i].sec,l)));
b.push_back(mp(poly(1),r+1));
return b;
}
func dp[MAXN][MAXN];
int L[MAXN],R[MAXN];
void dfs(int l,int r)
{
if (dp[l][r].size()) return;
dp[l][r].push_back(mp(poly(1),0));
if (l>r) return (void)(dp[l][r][0].fir[0]=1);
for (int k=l;k<=r;k++)
if (-2<=l+r-2*k&&l+r-2*k<=2)
{
dfs(l,k-1),dfs(k+1,r);
func a=dp[l][k-1],b=dp[k+1][r];
if (l<k) a=a+integ(a);
if (k<r) b=integ(b);
dp[l][r]=dp[l][r]+cut(a*b,L[k],R[k]);
}
}
int main()
{
inv[1]=1;
for (int i=2;i<MAXN;i++) inv[i]=(ll)inv[MOD%i]*(MOD-MOD/i)%MOD;
int n;
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d%d",&L[i],&R[i]);
dfs(1,n);
printf("%d\n",integ(dp[1][n]).back().fir[0]);
return 0;
}