Address
Solution
- 嘗試簡化第三個限制,設 ,我們取前 個數作爲第一個集合,取後 個數作爲第二個集合,因爲這兩個集合的差是所有選集合的方案中最小的,所以只要這兩個集合滿足條件,所有的集合都能滿足條件。
Algorithm 1
-
因爲 的奇偶性會影響中間是否會多出一個數,我們分情況討論:
-
若 是偶數,枚舉第 個數爲 ,那麼對剩下的數有如下要求:
- 與前 個數的差 且差遞減;
- 後 個數與 的差 且差遞增;
- 與前 個數的差的和加上後 個數與 的差的和小於 。
設 表示已經確定了前 個數、 與這些數的差的和爲 的方案數。
轉移即從大到小枚舉差 ,令 。
由於第二維 ,存在合法方案時一定有 ,時間複雜度 。
後 個數的方案可以看做是選取若干個後綴區間
+1
,設 表示已經選了 個後綴、這些後綴的長度之和爲 的方案數。轉移即從大到小枚舉後綴長度 ,令 ,時間複雜度同樣是 。
最後的答案即爲:
預處理 第二維的前綴和即可 計算。 -
若 是奇數,枚舉第 個數爲 ,只要最後算答案時把 改爲 ,其它部分與偶數的情況完全相同。
-
-
總的時間複雜度 。
Code 1
#include <bits/stdc++.h>
const int N = 5005;
int f[N][N], g[N][N];
int n, ans, mod;
inline void add(int &x, int y)
{
x += y;
x >= mod ? x -= mod : 0;
}
int main()
{
scanf("%d%d", &n, &mod);
int k = n - 1 >> 1;
f[0][0] = g[0][0] = 1;
for (int c = n - 1; c >= 0; --c)
for (int i = 0, im = !c ? n : n / c; i <= im; ++i)
for (int j = 0; j <= n - c; ++j)
add(f[i + 1][j + c], f[i][j]);
for (int c = k; c >= 0; --c)
for (int i = 0, im = !c ? n : n / c; i <= im; ++i)
for (int j = 0; j <= n - c; ++j)
add(g[i + 1][j + c], g[i][j]);
for (int i = 0; i <= n; ++i)
for (int j = 1; j <= n; ++j)
add(g[i][j], g[i][j - 1]);
if (!(n & 1))
{
for (int x = 1; x <= n; ++x)
for (int j = 0; j < x; ++j)
ans = (1ll * f[k + 1][j] * g[n - x][x - j - 1] + ans) % mod;
}
else
{
for (int x = 1; x <= n; ++x)
for (int j = 0; j < x; ++j)
ans = (1ll * f[k][j] * g[n - x][x - j - 1] + ans) % mod;
}
printf("%d\n", ans);
return 0;
}
Algorithm 2
-
考慮將 數組差分,去掉單調性的限制,令差分數組爲 。
-
令 ,則 ,因此其中一個限制條件就是 。
-
同樣對 的奇偶性進行討論:
-
若 爲奇數,,列出限制條件:
簡單移項後,我們可以得到:
其中:
-
若 爲偶數,同樣可以推出 的表達式:
整理一下,我們有:
注意到只有 是負數,考慮列出所有和 有關的限制條件:
在已知 的情況下,合法的 的個數爲:
-
-
設 表示 的方案數,最後的答案就爲 。
-
顯然 的轉移是一個完全揹包,時間複雜度 。
Code 2
#include <bits/stdc++.h>
const int N = 1e4 + 5;
int n, mod, ans, c[N], f[N];
inline void add(int &x, int y)
{
x += y;
x >= mod ? x -= mod : 0;
}
int main()
{
scanf("%d%d", &n, &mod);
int half = n >> 1;
for (int i = 2; i <= half + 1; ++i)
c[i] = i - 1;
for (int i = half + 2; i <= n; ++i)
c[i] = n - i + 2;
f[0] = 1;
for (int i = 2; i <= n; ++i)
for (int j = c[i]; j < n; ++j)
add(f[j], f[j - c[i]]);
for (int j = 0; j < n; ++j)
ans = (1ll * (n - j) * f[j] + ans) % mod;
printf("%d\n", ans);
return 0;
}