传送门
N
O
I
2019
NOI 2019
NOI2019 的第5道,听说可以用下降幂维护,但是我用拉格朗日插值怼过去了
考虑暴力的
d
p
dp
dp,
f
l
,
r
,
k
f_{l,r,k}
fl,r,k表示区间
[
l
,
r
]
[l,r]
[l,r] 的最大值为
k
k
k 的方案数
枚举最高点位置,显然并没有东西可以挡住最高点,因此
如果区间为奇数,
p
∈
[
m
i
d
−
1
,
m
i
d
+
1
]
p\in [mid-1,mid+1]
p∈[mid−1,mid+1]
如果区间为偶数,
p
∈
[
m
i
d
,
m
i
d
+
1
]
p\in [mid,mid+1]
p∈[mid,mid+1]
f
l
,
r
,
k
=
∑
x
≤
k
f
l
,
p
−
1
,
x
+
∑
x
<
k
f
p
+
1
,
r
,
x
f_{l,r,k}=\sum_{x\le k}f_{l,p-1,x}+\sum_{x<k} f_{p+1,r,x}
fl,r,k=∑x≤kfl,p−1,x+∑x<kfp+1,r,x
然后发现
p
p
p 每次取的值很少,于是总的区间个数
M
M
M应该不会很多
于是就有
O
(
M
V
)
O(MV)
O(MV) 的算法
考虑
A
i
=
1
,
B
i
=
1
e
9
A_i=1,B_i=1e9
Ai=1,Bi=1e9 ,转移十分的单一,猜想它是一个关于
k
k
k 的多项式
首先对于
l
=
r
l=r
l=r,
f
f
f 是一个常数,然后每次将
f
f
f 前缀和后加起来就会多一次,两边乘起来次数相当与加起来,
f
f
f 是一个不超过
r
−
l
+
1
r-l+1
r−l+1 次的多项式
考虑先按权值排序,分段,
[
v
i
−
1
,
v
i
−
1
]
[v_{i-1},v_i-1]
[vi−1,vi−1] 的转移类似
A
i
=
1
,
B
i
=
1
e
9
A_i=1,B_i=1e9
Ai=1,Bi=1e9
我们只需要暴力维护
n
n
n 项
d
p
dp
dp 的值,然后可以通过拉格朗日插出
v
i
−
1
v_i-1
vi−1 的
d
p
dp
dp 值
复杂度
O
(
n
2
m
)
O(n^2m)
O(n2m),
l
u
o
g
u
luogu
luogu 开
O
2
O_2
O2可以A
#include<bits/stdc++.h>
#define N 305
#define M 3005
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = (cnt << 1) + (cnt << 3) + (ch-'0'), ch = getchar();
return cnt * f;
}
#define cs const
cs int Mod = 1e9 + 7;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b;}
int mul(int a, int b){ return 1ll * a * b % Mod;}
int power(int a, int b){int ans=1; for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans,a); return ans;}
void Add(int &a, int b){ a = add(a, b);}
int home[N][N], dp[M][N], len[M];
bool vis[N][N];
int n, m, bench, a[N], b[N];
int e[N << 1], tot, Max;
int lim, x[N]; // lagrange
int fac[N], inv[N];
void prework(){
fac[0] = fac[1] = inv[0] = inv[1] = 1;
for(int i = 2; i <= lim; i++) fac[i] = mul(fac[i-1], i);
inv[lim] = power(fac[lim], Mod-2);
for(int i = lim-1; i >= 2; i--) inv[i] = mul(inv[i+1], i+1);
}
void get(int now, int l, int r, int cur);
int work(int l, int r){
if(l > r || vis[l][r]) return home[l][r];
int mid = (l+r) >> 1;
if(home[l][r] == 0){
home[l][r] = ++m;
len[m] = r-l+1;
} int now = home[l][r];
for(int i = 1; i <= Max; i++) dp[now][i] = 0;
if(l == r){
for(int i = 1; i <= Max; i++)
dp[now][i] = dp[now][i-1] + (i + bench >= a[l] && i + bench <= b[l]);
return now;
}
if(mid - l == r - mid){ get(now, l, r, mid - 1); get(now, l, r, mid); get(now, l, r, mid + 1); }
else{ get(now, l, r, mid); get(now, l, r, mid + 1);}
for(int i = 1; i <= Max; i++) Add(dp[now][i], dp[now][i-1]);
vis[l][r] = 1;
return now;
}
void get(int now, int l, int r, int cur){
int u = work(l, cur - 1), v = work(cur + 1, r);
for(int i = 1; i <= Max; i++)
if(i + bench >= a[cur] && i + bench <= b[cur])
Add(dp[now][i], mul(dp[u][i], dp[v][i-1]));
}
int lagrange(int n, int *x, int *y, int pos){
static int pre[N], suf[N];
pre[0] = add(pos, Mod - x[0]);
suf[n] = add(pos, Mod - x[n]);
for(int i = 1; i <= n; i++) pre[i] = mul(pre[i-1], add(pos, Mod - x[i]));
for(int i = n-1; i >= 0; i--) suf[i] = mul(suf[i+1], add(pos, Mod - x[i]));
int ans = 0;
for(int i = 0; i <= n; i++){
int tmp = 1;
if(i) tmp = mul(tmp, mul(inv[i], pre[i-1]));
if(i^n) tmp = mul(tmp, mul(inv[n-i], suf[i+1]));
if((n-i) & 1) tmp = Mod - tmp; ans = add(ans, mul(y[i], tmp));
} return ans;
}
int main(){
n = read(); lim = n + 3; prework();
for(int i = 1; i <= n; i++){
a[i] = read(), b[i] = read();
Max = max(Max, b[i]);
e[++tot] = a[i], e[++tot] = b[i] + 1;
} sort(e + 1, e + tot + 1);
for(int i = 0; i <= lim; i++) dp[0][i] = 1;
for(int i = 1; i <= tot; i++){
if(e[i] != e[i-1]){
bench = e[i-1];
Max = min(lim, e[i] - e[i-1] - 1);
memset(vis, 0, sizeof(vis));
work(1, n);
if(Max == e[i] - e[i-1] - 1){
for(int j = 1; j <= m; j++)
dp[j][0] = dp[j][Max];
} else{
static int x[N];
for(int j = 0; j <= lim; j++)
x[j] = bench + j;
for(int j = 1; j <= m; j++){
dp[j][0] = lagrange(len[j] + 2, x, dp[j], e[i] - 1);
}
} bench = e[i] - 1; Max = 1;
memset(vis, 0, sizeof(vis));
work(1, n);
for(int j = 1; j <= m; j++)
dp[j][0] = dp[j][Max];
}
} cout << dp[home[1][n]][0]; return 0;
}