题解 SP3734 【PERIODNI - Periodni】
SPOJ PERIODNI
不知道公式会不会出问题,建议到 这里 或者 Luogu 博客查看。
仍然考虑类笛卡尔树的 dp,我们把整个直方图画成了很多个矩形。首先考虑一个矩形内部的答案,如果这个矩形
然后考虑设
怎么转移?我们先抛开这个矩形本身,只考虑
对于所有儿子分别跑一次之后,当前的
其中
复杂度,如果笛卡尔树暴力枚举下一层暴力跑树形 dp 都才
#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "set"
#include "vector"
#include "map"
#include "cmath"
#define MAXN 506
//#define int long long
using namespace std;
typedef long long ll;
#define P 1000000007
int n , m;
int Pow( int x , int a ) {
int ans = 1 , cur = x % P;
while( a ) {
if( a & 1 ) ans = 1ll * ans * cur % P;
cur = 1ll * cur * cur % P , a >>= 1;
}
return ans;
}
int h[MAXN];
int J[1000006] , invJ[1000006];
int C( int a , int b ) {
if( a < b ) return 0;
return 1ll * J[a] * invJ[b] % P * invJ[a - b] % P;
}
int S( int n , int m , int k ) {
return 1ll * C( n , k ) * C( m , k ) % P * J[k] % P;
}
int dp[506 * 506][506] , cur , k;
int solve( int l , int r , int base ) {
if( l > r ) return 0;
int re = 0 , c = ++ cur , L = r - l + 1;
for( int i = l ; i <= r ; ++ i ) if( !re || h[i] < h[re] ) re = i;
vector<int> mn;
mn.push_back( l - 1 );
for( int i = l ; i <= r ; ++ i ) if( h[i] == h[re] ) mn.push_back( i );
mn.push_back( r + 1 );
dp[c][0] = 1;
for( int i = 1 ; i < mn.size() ; ++ i ) {
int t = mn[i] , p = mn[i - 1] , l = t - p - 1 , id = solve( p + 1 , t - 1 , h[re] );
if( id == 0 ) continue;
for( int j = min( L , k ) ; j >= 0 ; -- j )
for( int k = 1 ; k <= min( j , l ) ; ++ k )
( dp[c][j] += 1ll * dp[id][k] * dp[c][j - k] % P ) %= P;
}
int dh = h[re] - base;
for( int i = min( L , k ) ; i >= 0 ; -- i )
for( int j = 1 ; j <= min( dh , i ) ; ++ j )
( dp[c][i] += 1ll * dp[c][i - j] * S( dh , L - ( i - j ) , j ) % P ) %= P;
return c;
}
int main() {
J[0] = 1; invJ[0] = 1;
for( int i = 1 ; i < 1000006 ; ++ i ) J[i] = 1ll * J[i - 1] * i % P , invJ[i] = Pow( J[i] , P - 2 );
cin >> n >> k;
for( int i = 1 ; i <= n ; ++ i ) scanf("%d",h + i);
int c = solve( 1 , n , 0 );
cout << dp[c][k] << endl;
}