NOI2019 機器人
NOI2019 機器人
考慮最右的最大值,顯然其將序列分成兩端,所以可以區間 dp,設 \(f_{l,r,k}\) 表示區間 \([l,r]\),最大值為 \(k\) 的方案數,顯然有轉移 \(f_{l,r,k}=\sum_{mid,k'\le k,j<k} f_{l,mid-1,k'}\times f_{mid+1,r,j}\),對於每個區間合法的選擇最大值的位置只有偏中間的 \(4\) 個,所以這一部分複雜度為 \(\mathcal O(n^2W)\)
然後會發現有效的區間不多,將 dp 換成記搜就可以過這檔部分分了,據說有效區間只有 \(2000\) 個左右。
接下來考慮類似於划艇的做法,將權值區間進行分段(將每個區間視為一個左閉右開的區間 \([l,r)\)
容易發現對於相同的段,顯然有轉移是相同的,同時考慮 \(f_{i,i,k\in [l,r)}\) 時的取值,顯然都是 \(1\)
每次轉移等價於統計一個字首和並乘起來,考慮將轉移按照段落進行劃分,對於跨段的轉移,對於同段內的每個元素 \(i'\) 其均相同,將之視為常數,那麼對於 \(i'\) 產生影響的僅有同段的 \(j\) 滿足 \(j<i'\),此時轉移則等價於在區間內記錄字首,然後再做點值乘法,注意到在 \(l=r\) 時這是一個常數函式,而轉移本質上在做字首和並進行點值乘法,換而言之本質上這是多項式卷積,次數會增高,簡單歸納會發現對於區間 \([l,r]\)
於是轉移只需要取 \(n\) 個點值並相應乘起來,暴力做的話複雜度非常的 \(\mathcal O(2000\times n^2)\),聽說跑不滿,我就試著寫一發算了。
然後如果直接維護點值,然後暴力插值,可以跑 \(\rm 95pts\),但是這樣每個區間 \([l,r]\) 必須要維護 \(n\) 個點值,我大概要跑 5s,不知道有沒有老哥可以通過卡常卡過去。
然而由於題目的性質,大區間的出現次數非常的少,大部分割槽間都是小區間,所以我們需要去利用每個區間 \([l,r]\)
考慮直接去維護這個多項式,對於每個區間維護一個多項式 \(A_{l,r}(x)\),那麼轉移本質上在進行多項式卷積,計算答案只需要對多項式進行離散積分然後就可以計算答案了。
通過普通冪維護非常的麻煩,所以可以通過下降冪去維護這個多項式,下降冪的離散積分是非常優美的:\(\int x^{\underline{n}}=\frac{x^{\underline{n+1}}}{n+1}\),轉移仍然是暴力卷積,複雜度是玄學。
然後轉移的時候只需要做下降冪多項式乘法即可,方法很簡單,通過乘以 \(e^{x}\) 暴力轉 EGF 點值,在乘以 \(e^{-x}\) 還原即可。
事實上沒有必要卷積,下降冪轉點值與係數都是非常方便的,複雜度是 \(\mathcal O(n\cdot \sum |\rm len|^2)\)
\(Code:\)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
#define pb push_back
#define int long long
int gi() {
char cc = getchar() ; int cn = 0, flus = 1 ;
while( cc < '0' || cc > '9' ) { if( cc == '-' ) flus = - flus ; cc = getchar() ; }
while( cc >= '0' && cc <= '9' ) cn = cn * 10 + cc - '0', cc = getchar() ;
return cn * flus ;
}
const int N = 300 + 5 ;
const int V = 1500 + 5 ;
const int P = 1e9 + 7 ;
int n, D, dx, top, m, fac[V], inv[V], finv[V], A[N], B[N], K[V], st[V], vis[N][N] ;
int cnt, op, Dp[2205][V], Id[N][N] ;
long long ffS, iv[V] ;
struct node {
int x, y ;
} qwq[V] ;
vector<int> dp[2005] ;
int abc( int x ) {
return ( x > 0 ) ? x : -x ;
}
void inc( int &x, int y ) {
( ( x += y ) >= P ) && ( x -= P ) ;
}
int fpow( int x, int k ) {
int ans = 1, base = x ;
while(k) {
if( k & 1 ) ans = 1ll * ans * base % P ;
base = 1ll * base * base % P, k >>= 1 ;
} return ans ;
}
int Lag( int u, int x, int tp ) {
int fp = 1, ans = 0 ;
for( re int i = 0; i < tp; ++ i ) {
ans = ( ans + 1ll * fp * dp[u][i] % P ) % P,
fp = 1ll * fp * ( x - i ) % P ;
} return ans ;
}
int ffp[V], Ex[V] ;
inline void dft( int u ) {
int l = dp[u].size(), le = l + 6 ;
for( re int i = 0; i < l + le; ++ i )
for( re int j = 0; j <= i && j < l; ++ j ) {
Ex[i] += dp[u][j] * inv[i - j] % P ;
}
dp[u].resize(l + l + 5) ;
for( re int i = 0; i < dp[u].size(); ++ i ) dp[u][i] = Ex[i] % P * fac[i] % P ;
for( re int i = 0; i < l + le; ++ i ) Ex[i] = 0 ;
}
inline void idft( int u ) {
int l = dp[u].size() ;
for( re int i = 0; i < l; ++ i ) dp[u][i] = dp[u][i] * inv[i] % P ;
for( re int i = 0; i < l; ++ i )
for( re int j = 0; j <= i; ++ j )
inc( Ex[i], dp[u][j] * finv[i - j] % P ) ;
for( re int i = 0; i < l; ++ i ) dp[u][i] = Ex[i] ;
for( re int i = 0; i < l; ++ i ) Ex[i] = 0 ;
}
void dfs( int l, int r, int L, int R ) {
if( l > r || vis[l][r] == op ) return ;
int ft = R - L + 1 ;
vis[l][r] = op ; int u = Id[l][r] ;
int lx = max( l, ( r + l - 2 ) / 2 ), rx = min( r, ( r + l + 2 ) / 2 ) ;
dp[u].clear() ;
if( l == r ) {
dp[u].pb(Dp[u][op - 1]) ; int ff = 0 ;
if( A[l] <= L && R <= B[l] ) dp[u].pb(1), ++ ff ;
Dp[u][op] = Lag(u, ft, ff + 1 ), dft(u) ;
dp[u].resize(2 * (r - l) + 4) ;
return ;
}
dp[u].resize(r - l + 3) ;
for( re int i = lx; i <= rx; ++ i ) {
int ll = i - l, rr = r - i ;
if( abc(rr - ll) > 2 ) continue ;
dfs( l, i - 1, L, R ), dfs( i + 1, r, L, R ) ;
if( A[i] > L || B[i] < R ) continue ;
if( i == l )
for( re int j = 1; j <= r - l + 2; ++ j )
inc( dp[u][j], dp[Id[i + 1][r]][j - 1] ) ;
else if( i == r )
for( re int j = 1; j <= r - l + 2; ++ j )
inc( dp[u][j], dp[Id[l][i - 1]][j] ) ;
else
for( re int j = 1; j <= r - l + 2; ++ j )
inc( dp[u][j], 1ll * dp[Id[l][i - 1]][j] * dp[Id[i + 1][r]][j - 1] % P ) ;
}
for( re int i = 1; i <= r - l + 2; ++ i ) inc( dp[u][i], dp[u][i - 1] ) ;
for( re int i = 0; i <= r - l + 2; ++ i ) inc( dp[u][i], Dp[u][op - 1] ) ;
dp[u].resize(r - l + 2), idft(u) ;
Dp[u][op] = Lag(u, ft, r - l + 2), dft(u) ;
dp[u].resize(min( n + 2, 2 * (r - l) + 4)) ;
}
void Dfs( int l, int r ) {
if( l > r || Id[l][r] ) return ;
Id[l][r] = ++ cnt ; if( l == r ) return ;
int lx = max( l, ( r + l - 2 ) / 2 ), rx = min( r, ( r + l + 2 ) / 2 ) ;
for( re int i = lx; i <= rx; ++ i ) {
int ll = i - l, rr = r - i ;
if( abc(rr - ll) > 2 ) continue ;
Dfs( l, i - 1 ) ; Dfs( i + 1, r ) ;
}
}
signed main()
{
n = gi(), D = n + 50 ; fac[0] = inv[0] = iv[0] = 1 ;
rep( i, 1, n ) A[i] = gi(), B[i] = gi(), K[++ top] = A[i], K[++ top] = B[i] + 1 ;
rep( i, 1, D ) fac[i] = 1ll * fac[i - 1] * i % P, inv[i] = fpow( fac[i], P - 2 ) ;
rep( i, 1, D ) iv[i] = fpow( i, P - 2 ) ;
rep( i, 0, D ) finv[i] = (i & 1) ? P - inv[i] : inv[i] ;
sort( K + 1, K + top + 1 ) ;
m = top, top = 0 ;
rep( i, 1, m ) if( K[i] != K[i - 1] ) st[++ top] = K[i] ;
Dfs( 1, n ) ;
for( re int i = 2; i <= top; ++ i )
op = i, dx = min( D, st[i] - st[i - 1] ), dfs( 1, n, st[i - 1], st[i] - 1 ) ;
printf("%lld\n", Dp[Id[1][n]][op] % P ) ;
return 0 ;
}