【BZOJ2616】SPOJ PERIODNI 笛卡爾樹+樹形DP
阿新 • • 發佈:2017-11-05
方法 logs pac 題解 ring pri -i nbsp 表示
2 3 1 2 4
【BZOJ2616】SPOJ PERIODNI
Description
Input
第1行包括兩個正整數N,K,表示了棋盤的列數和放的車數。
第2行包含N個正整數,表示了棋盤每列的高度。
Output
包括一個非負整數,表示有多少種放置的方案,輸出答案mod
1000000007後的結果即可。
Sample Input
5 22 3 1 2 4
Sample Output
43HINT
對於100%的數據,有 N≤500,K≤500,h[i] ≤1000000。
題解:一看題就感覺應該是單調棧什麽的。。。具體地說,是笛卡爾樹。於是學了一發笛卡爾樹的建樹方法,感覺跟建虛樹差不多。
我們用一個棧來維護笛卡爾樹上當前的一條鏈(向右的鏈),然後對於第i個點,我們看一下它會被插入到鏈的哪個位置,這個位置下面的點都連接到i的左兒子處,然後將i入棧。
本題的笛卡爾樹要滿足每個父親的高度都比兒子小。
然後考慮樹形DP,我們將笛卡爾樹上的每個節點看成原圖的一個矩形,它的上界是它自己的深度,下界是它父親的深度,左右邊界是它的子樹範圍。用f[x][y]表示在x的子樹中放j個車使其互補影響的方案數。我們只需要考慮x對應的矩形中放多少點以及如何防即可。
用一點組合知識便可得到轉移方程,設矩形的長為n,寬為m,當前兒子是a,則:
$f[x][y]=\sum\limits_{b}{f[a][y-b]*f[x][b]}$
再乘上矩形中如何放:
$f[x][z]=\sum\limits_{y}f[x][z-y]*y!C_{n-z+y}^yC_m^y$
#include <cstdio> #include <cstring> #include <iostream> #include <vector> using namespace std; typedef long long ll; const ll P=1000000007; int n,m,tot,top,rt; ll f[510][510],jc[1000010],ine[1000010],jcc[1000010],g[510]; int st[510],fa[510],v[510],ch[510][2]; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘) f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+(gc^‘0‘),gc=getchar(); return ret*f; } inline ll c(int a,int b) { if(a<b) return 0; return jc[a]*jcc[a-b]%P*jcc[b]%P; } int dfs(int x) { int a=1,b=v[x]-v[fa[x]],i,j,k,y,aa; f[x][0]=1; for(i=0;i<=1;i++) if(ch[x][i]) { y=ch[x][i],aa=dfs(y); memset(g,0,sizeof(g)); for(j=0;j<=a;j++) for(k=0;k<=aa&&j+k<=m;k++) g[j+k]=(g[j+k]+f[x][j]*f[y][k])%P; a+=aa; for(j=0;j<=a;j++) f[x][j]=g[j]; } for(i=min(m,a);i>=0;i--) { ll tmp=0; for(j=0;j<=i;j++) tmp=(tmp+f[x][i-j]*jc[j]%P*c(a-i+j,j)%P*c(b,j)%P)%P; f[x][i]=tmp; } return a; } int main() { n=rd(),m=rd(); int i,a; for(i=1;i<=n;i++) v[i]=rd(); ine[0]=ine[1]=jc[0]=jc[1]=jcc[0]=jcc[1]=1; for(i=2;i<=1000000;i++) jc[i]=jc[i-1]*i%P,ine[i]=(P-ine[P%i]*(P/i)%P)%P,jcc[i]=jcc[i-1]*ine[i]%P; for(i=1;i<=n;i++) { while(top&&v[st[top]]>v[i]) { a=st[top],top--; if(top&&v[st[top]]>v[i]) ch[st[top]][1]=a,fa[a]=st[top]; else ch[i][0]=a,fa[a]=i; } st[++top]=i; } while(top>1) ch[st[top-1]][1]=st[top],fa[st[top]]=st[top-1],top--; rt=st[1],dfs(rt); printf("%lld",f[rt][m]); return 0; }
【BZOJ2616】SPOJ PERIODNI 笛卡爾樹+樹形DP