1. 程式人生 > >[BZOJ2616][SPOJ PERIODNI] Periodni (樹形背包)

[BZOJ2616][SPOJ PERIODNI] Periodni (樹形背包)

delta 之前 $$ temp %d 要求 AR amp using

BZOJ傳送門

SPOJ傳送門

簡易題意

有$n$個$1 \times h_i$的空地,順次拼成一個大的空地,其中不是空地的地方就是墻。現在要求在空地上放車,要求車不能互相攻擊到。

數據範圍

$n,m\leq500?h_i\leq1000000$

題解

這題第一感覺是個暴力hash+DP,使用組合數進行轉移。

首先先想按列考慮,但是按列考慮空地的高度有可能增長也有可能降低,因此不方便進行轉移。

換個思路,我們可以按行考慮。

如果每次從高往低掃,每次截取當前這條線以上的空地圖形,與之前相比,會產生新的聯通塊或者合並舊的聯通塊。

合並舊的聯通塊當且僅當這個位置的高度是之前兩個舊的聯通塊內最小的之。

我們可以反方向考慮,從合並聯通塊變成分裂聯通塊。

每次分裂的時候,找到這一段內最小的值,之後將其分裂。

對於每一段,我們定義$dp_{j}$表示在當前聯通塊(段)放$j$個車時的方案數。

容易看出,每次轉移的時候,新聯通塊一定是一堆舊的聯通塊加上一個矩形

技術分享圖片

對於新的聯通塊,我們並不在意上面幾個聯通塊哪些位置放了車,只在意有幾列被放了車,即放了幾個車。

因此,我們合並的時候先統計舊的聯通塊有多少種合法情況放了$i$個車,這很明顯是一個使用背包的計數題。

處理完上面的聯通塊後,我們再處理下面新增的矩形。

我們只需要枚舉上面聯通塊車的個數,和下面聯通塊車的個數,就可以利用組合數算出情況個數。
$$
dp_{i+j}=\sum dp_i\times C_{\Delta h}^j \times A_{len}^j

$$
(似乎$dp_0$要特殊處理)

答案就是總段的$dp_k$

代碼

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
#include<bitset>
using namespace std;
template<typename __T>
inline void read(__T &x)
{
    x=0;
    int f=1;char c=getchar();
    while
(!isdigit(c)){if(c=='-') f=-1;c=getchar();} while(isdigit(c)) {x=x*10+c-'0';c=getchar();} x*=f; } const int mod=1000000007; int frac[1000005]; int fracinv[1000005]; long long qpow(long long a,long long b) { long long ans=1; while(b) { if(b&1) ans=(ans*a)%mod; b>>=1; a=a*a%mod; } return ans; } int C(int n,int m) { if(n<m) return 0; return 1ll*frac[n]*fracinv[n-m]%mod*fracinv[m]%mod; } int Cinv(int n,int m) { return 1ll*fracinv[n]*frac[n-m]%mod*frac[m]%mod; } int A(int n,int m) { return 1ll*frac[n]*fracinv[n-m]%mod; } int h[505]; int n,k; int dp[505][505]; int tmp[505]; int tot=1; void dfs(int l,int r,int id,int lasth) { int minz=12345678; for(int i=l;i<=r;i++) minz=min(minz,h[i]); int top=0; int sta[505]; for(int i=l;i<=r;i++) if(h[i]==minz) sta[top++]=i; if(top==r-l+1) { for(int i=0;i<=top;i++) dp[id][i]=1ll*C(minz-lasth,i)*A(top,i)%mod; return; } int laspos=l; int edpos=l; dp[id][0]=1; while(laspos<=r) { while(h[laspos]==minz) laspos++; if(laspos>r) break; edpos=laspos; while(edpos<r && h[edpos+1]>minz) edpos++; int neid=++tot; dfs(laspos,edpos,neid,minz); memcpy(tmp,dp[id],4*k+4); memset(dp[id],0,4*k+4); for(int i=0;i<=laspos;i++) for(int j=0;j<=edpos-laspos+1;j++) dp[id][i+j]=(dp[id][i+j]+1ll*tmp[i]*dp[neid][j])%mod; dp[id][0]=1; laspos=edpos+1; } memcpy(tmp,dp[id],4*k+4); memset(dp[id],0,4*k+4); for(int i=0;i<=r-l+1-top;i++) for(int j=0;j<=r-l+1-i;j++) dp[id][i+j]=(dp[id][i+j]+1ll*tmp[i]*C(minz-lasth,j)%mod*A(r-l+1-i,j))%mod; dp[id][0]=1; } int main() { read(n); read(k); int sbsx=n; for(int i=0;i<n;i++) { read(h[i]); sbsx=max(sbsx,h[i]); } frac[0]=1; for(int i=1;i<=sbsx;i++) frac[i]=1ll*frac[i-1]*i%mod; fracinv[sbsx]=qpow(frac[sbsx],mod-2); for(int i=sbsx-1;i>0;i--) fracinv[i]=1ll*fracinv[i+1]*(i+1)%mod; fracinv[0]=1; dfs(0,n-1,1,0); printf("%d\n",dp[1][k]); return 0; }

參考

沒有

[BZOJ2616][SPOJ PERIODNI] Periodni (樹形背包)