【笛卡爾樹+DP】【SP3734】 PERIODNI - Periodni

CE自動機發表於2020-10-01

題目

題目描述
English VietnameseLuka is bored in chemistry class so he is staring at a large periodic table of chemical elements hanging from a wall above the blackboard. To kill time, Luka decided to make his own table completely different from the one in the classroom.

His table consists of N columns, each with some height, aligned at the bottom (see example below). After he draws the table he needs to fill it with elements. He first decided to enter the noble gases of which there are K. Luka must put them in the table so that no two noble gases are close to each other.

Two squares in the table are close to each other if they are in the same column or row, and all squares between them exist. In the example below, the ‘a’ squares are not close, but the ‘b’ squares are.

Write a program that, given N, K and the heights of the N columns, calculates the total number of ways for Luka to place the noble gases into the table. This number can be large, so output it modulo 1 000 000 007.

輸入格式
The first line contains the integers N and K separated by a space (1

The next line contains N positive integers, separated by spaces. These are heights of the columns from left to right. The heights will be at most 1 000 000.

輸出格式
Output the number of ways for Luka to fill his table with noble gases, modulo 1 000 000 007.

題意翻譯
給定一個N列的表格,每列的高度各不相同,但底部對齊,然後向表格中填入K個相同的數,填寫時要求不能有兩個數在同一列,或同一行,下圖中b是錯誤的填寫,a是正確的填寫,因為兩個a雖然在同一行,但它們中間的表格斷開。

輸出所有填寫方案數對1 000 000 007的餘數。

輸入: 第一行兩個整數 N 和 K (1 ≤ N ≤ 500, 1 ≤ K ≤ 500),表示表格的列數和要填寫的數的個數。 接下來一行N個數,表示每列的高度。高度不超過 1 000 000.

輸出: 一個整數,方案總數對1000 000 007的餘數。

注意: 對於 40% 的資料, 所有數值小於15. 對於70% 的資料,所有數值小於100.

Translated by @yijan

輸入輸出樣例
輸入 #1複製
5 2
2 3 1 2 4
輸出 #1複製
43

思路

顯然是先建出小根笛卡爾樹,考慮每個矩形內部的答案。
d p [ u ] [ i ] dp[u][i] dp[u][i] 表示 u u u 子樹內放 i i i 個數的方案數, d p 1 [ i ] dp1[i] dp1[i] 表示 當前子樹 u u u 內不考慮當前矩形,放 i i i 個數的方案數,設 H [ i ] H[i] H[i] 為當前矩陣可行高度(即 A [ u ] − A [ f a [ u ] ] A[u]-A[fa[u]] A[u]A[fa[u]] )。
顯然有 d p 1 [ ] = f [ l s ] ∗ f [ r s ] dp1[] = f[ls]*f[rs] dp1[]=f[ls]f[rs] ,即左右子樹的卷積。
接下來就是揹包的轉移了,列舉當前矩形內有多少列還是空的進行轉移。
設當前子樹放置 i i i 個棋子,有 j j j 個在當前矩陣放置。
d p [ u ] [ i ] + = ∑ j = 0 i d p 1 [ i − j ] ∗ C ( S z [ u ] − ( i − j ) , j ) ∗ C ( H [ x ] , j ) ∗ j ! dp[u][i]+=\sum_{j=0}^idp1[i-j]*C(Sz[u]-(i-j),j)*C(H[x],j)*j! dp[u][i]+=j=0idp1[ij]C(Sz[u](ij),j)C(H[x],j)j!
第一個組合數是列舉矩陣所剩的行,第二個組合數是列舉矩陣所剩的列。
最後乘上 j! 是因為橫縱座標是兩兩組合的,因此匹配的方案數為 j!。

程式碼

#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define N 5077
#define mod 1000000007
ll n,k,top,root;
ll ls[N],rs[N],st[N];
ll f[N][N],h[N],siz[N],fac[1000050],inv[1000050];
ll qp(ll x,ll y)
{
    ll ans=1;
    while(y)
    {
        if(y&1) ans=(ans*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return ans%mod;
}
void init()
{
    fac[0]=fac[1]=inv[0]=inv[1]=1;
	fac[2]=2,inv[2]=qp(2,mod-2);
	for(int i=3;i<=1000000;++i)
	{
		fac[i]=(fac[i-1]*i)%mod;
		inv[i]=qp(fac[i],mod-2);
	}
}
ll C(ll n,ll m)
{
    if(n<m) return 0;
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int build()
{
    for(int i=1;i<=n;++i)
    {
        while(top&&h[st[top]]>h[i]) ls[i]=st[top--];
        if(top) rs[st[top]]=i;
        st[++top]=i;
    }
    return st[1];
}
void dfs(int x,int val)
{
    f[x][0]=siz[x]=1;
    ll high=h[x]-val;
    if(ls[x])
    {
        ll y=ls[x];
        dfs(y,h[x]),siz[x]+=siz[y];
        for(ll i=min(siz[x],k);i>=0;--i)
            for(ll j=1;j<=min(siz[y],i);++j)
                f[x][i]=(f[x][i]+f[y][j]*f[x][i-j]%mod)%mod;
    }
    if(rs[x])
    {
        ll y=rs[x];
        dfs(y,h[x]),siz[x]+=siz[y];
        for(ll i=min(siz[x],k);i>=0;--i)
            for(ll j=1;j<=min(siz[y],i);++j)
                f[x][i]=(f[x][i]+f[y][j]*f[x][i-j]%mod)%mod;
    }
    for(ll i=min(siz[x],k);i>=0;--i)
        for(ll j=1;j<=min(high,i);++j)
            f[x][i]=(f[x][i]+f[x][i-j]*fac[j]%mod*C(high,j)%mod*C(siz[x]-(i-j),j)%mod)%mod;
}
int main()
{
    init();
    read(n),read(k);
    for(int i=1;i<=n;++i) read(h[i]);
    root=build();
    dfs(root,0);
    printf("%lld",f[root][k]);
	return 0;
}

相關文章