ZR979B 十聯測 Day 9 唯一睿醬

2022-04-05 20:14:15 字數 3616 閱讀 2177

給定乙個陣列\(r_i\),表明對於第\(i\)個數來說,他是\([max(1,i - r_i),min(n,i+r_i)]\)中最大的,求有多少\(1-n\)的排列的\(r _i\)與給定的相同

\(n \le 5000\)

列舉全排列即可

首先這個資料範圍要考慮狀壓dp,有關排列計數的題目,一般又要利用題目中給出的特殊性質,所以並沒有所謂的套路化思維可言

所以,因為這道題和排列的大小關係有關,所以我們考慮狀壓dp的時候從小往大地向裡面加數,設\(f_s\)表示現在\(s\)這些位置的數已經被填上了,同時滿足條件的方案數

轉移就列舉最後一次填了哪乙個數

由於我們是從小往大去填,對於對於所有的狀態為\(1\)的地方,都是比這個數要小的,同理\(0\)就是要大的,我們就可以輕鬆的計算出這個位置填最大值是否合法,進而轉移

時間複雜度:\(o(2^nn^2)\)

注意上一段中我加深的部分,我們狀壓dp的本質是判斷最大值在這個位置是否合法,這也在啟示我們多項式時間複雜度的做法:

列舉最大值,類似區間dp的合併

設\(f_\)表示\([l,r]\)這些位置的滿足條件的合法排列個數,注意這個地方的所謂的排列個數實際上是\(1\)到\((r - l + 1)\)的乙個排列,因為只關心大小關係的時候,排列的子集也可以看做乙個排列,在合併的時候通過組合計數合成乙個大的排列即可,那麼我們就列舉最大值的位置\(k\)來進行轉移,那麼首先這個\(k\)應該滿足$k - r_k = l \(或者\)k + r_k = r\(,否則就不可能成為最大值,其次還要滿足\)\forall i \in [l,k - 1],i+r_i < k\(和\)\forall i\in [k + 1,r],i -r_i>k$如果左右兩個區間存在乙個比他更大的數,也顯然不能轉移

接下來想如何合併兩個區間

你剛才說過,排列的子集也是乙個排列,所以我們就可以得到的方程

\[f_ = \sum_ f_ \times f_ \times \binom

\]首先,\(k\)應該滿足上述條件,\(\binom\)的意思是一共有\(r - l\)個數(因為最大值的位置是固定的),選擇\(k - l\)個放在左邊,注意邊界處理即可

首先,我們可以發現,乙個\(k\)最多也只可能會對\(k - r_k\)和\(k+r_k\)產生貢獻,所以我們直接

設\(g_l\)表示能對\(l\)產生貢獻的點,直接轉移即可省掉列舉最大值的位置優化到\(o(n ^2)\)

列舉最大值最小值可能是解題關鍵

首先排列的子集看做排列這一點在只關係大小關係的技術問題上十分耐用

在區間dp的時候,有時候\(f_\)的值要仔細斟酌

#include#include#include#include#include#include#include#include#include#include#include#define ll long long

#define pii pair#define mk make_pair

#define fi first

#define se second

using namespace std;

const int n = (1 << 17) + 50;

const int mod = 1e9 + 7;

int n,m;

int f[n];

int r[n];

inline int read()

while(isdigit(ch))

return v * c;

}inline int count(int x)

return res;

}inline int get(int x,int pos)

inline int mo(int x)

int main()

} printf("%d\n",f[(1 << n) - 1]);

return 0;

}

#include#include#include#include#include#include#include#include#include#include#include#define ll long long

#define pii pair#define mk make_pair

#define fi first

#define se second

using namespace std;

const int n = 5005;

const ll mod = 1e9 + 7;

ll f[n][n];

ll c[n][n];

int n;

int mr[n][n],ml[n][n];

int a[n];

inline int read()

while(isdigit(ch))

return v * c;

}inline int mo(ll x)

inline ll dfs(int l,int r)

} return f[l][r];

}int main()

for(int i = 1;i <= n;++i)

} ml[n + 1][n] = n + 1;

printf("%lld\n",dfs(1,n));

return 0;

}

#include#include#include#include#include#include#include#include#include#include#include#define ll long long

#define pii pair#define mk make_pair

#define fi first

#define se second

using namespace std;

const int n = 5005;

const ll mod = 1e9 + 7;

ll f[n][n];

int c[n][n];

int n;

int mr[n][n],ml[n][n];

int a[n];

vector gl[n << 1],gr[n << 1];

inline int read()

while(isdigit(ch))

return v * c;

}inline int mo(ll x)

inline ll dfs(int l,int r)

to = lower_bound(gr[r].begin(),gr[r].end(),l) - gr[r].begin();

for(int k = to;k < (int)gr[r].size();++k)

return f[l][r];

}int main()

for(int i = 1;i <= n;++i)

} ml[n + 1][n] = n + 1;

for(int i = 1;i <= n;++i)

for(int i = 1;i <= 2 * n;++i)

printf("%lld\n",dfs(1,n));

return 0;

}