1. 程式人生 > 實用技巧 >NOI2019 機器人

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)\)

),容易發現至多有 \(2n\) 個段。

容易發現對於相同的段,顯然有轉移是相同的,同時考慮 \(f_{i,i,k\in [l,r)}\) 時的取值,顯然都是 \(1\)

每次轉移等價於統計一個字首和並乘起來,考慮將轉移按照段落進行劃分,對於跨段的轉移,對於同段內的每個元素 \(i'\) 其均相同,將之視為常數,那麼對於 \(i'\) 產生影響的僅有同段的 \(j\) 滿足 \(j<i'\),此時轉移則等價於在區間內記錄字首,然後再做點值乘法,注意到在 \(l=r\) 時這是一個常數函式,而轉移本質上在做字首和並進行點值乘法,換而言之本質上這是多項式卷積,次數會增高,簡單歸納會發現對於區間 \([l,r]\)

,考慮段 \([L,R]\) 時答案是一個關於 \(k\)\(r-l\) 次多項式(請注意跨區間必然是常數項,所以會是一個固定的多項式)。

於是轉移只需要取 \(n\) 個點值並相應乘起來,暴力做的話複雜度非常的 \(\mathcal O(2000\times n^2)\),聽說跑不滿,我就試著寫一發算了。

然後如果直接維護點值,然後暴力插值,可以跑 \(\rm 95pts\),但是這樣每個區間 \([l,r]\) 必須要維護 \(n\) 個點值,我大概要跑 5s,不知道有沒有老哥可以通過卡常卡過去。

然而由於題目的性質,大區間的出現次數非常的少,大部分割槽間都是小區間,所以我們需要去利用每個區間 \([l,r]\)

是一個次數不超過 \(r-l\) 次多項式的這個性質。

考慮直接去維護這個多項式,對於每個區間維護一個多項式 \(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 ;
}