Link
https://www.lydsy.com/JudgeOnline/problem.php?id=2436
題意
安排 個活動,每個活動舉辦時間 ,同一時間兩個場地只能有一個辦活動(可以辦好多個)。
對於每個詢問,欽定一個活動必須舉辦(或者不欽定),要求最大化舉辦的活動數量較少的場地舉辦的活動數量。
輸出這個最大值。
好 我又老了
上來先排序離散化
考慮到實際上一個會場選的是“一段時間”,可以預處理出在某一段時間內開始並結束的活動數量
很容易根據上面這東西大概地對轉移有個猜想,然後就差不多可以設計出狀態:
可以考慮設類似於 ,那麼前 個時間,一個場地至多選 個的時候另一個場地至多選 個
爲了方便欽定,相應地考慮可能會設 ,那麼第 個時間之後,一個場地至多選 個的時候下略
考慮轉移,
那麼 和 都可以 搞出來
然後考慮不加限制時的答案:暴掃一遍都可以搞出來了(。。。
欽定一個活動 等於 欽定了那一整段時間內的都要選
然後兩邊的怎麼做?
如果我們欽定的給某一個場地辦,對於另外一個場地可以考慮枚舉在欽定左右分別選了幾個
好像也搞完了?
……?不對。
最終答案並不等於 。
(啊啊啊。。要是考起來我大概就被這東西坑爆了)
欽定了 ,但是實際上選的區域可能會更大 比如 這樣
顯然我們欽定還是有影響到 和
所以我們需要處理出所有的 ,實際上是 的
然後可以搞記憶化搞剪枝等等玄學一發小常數騙分(甚至可能水掉這道題
(我作爲一個不會水法選手 記筆記記筆記.jpg
(但是感覺我反而會玄學剪枝剪到常數變大啊(???
關於這部分中間差點掉坑,其實如果運氣好是可以避免的
思考欽定某個活動要辦,然後考慮枚舉一下某個場地覆蓋這個活動的那段啓用的時間
好像也挺自然的(?)
怎麼優化成 ?
dp 優化當然要考慮單調性啦,考慮
思考 最優決策點,那先固定
不難發現 和 在 固定的時候有顯然的單調性
換成
所以對於最優決策,按照 中的某一個排序後,按順序看 必定是一個變大一個變小
那麼維護指針就可以 找出所有最優決策點對
複雜度
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
using namespace std;
const int MAXN = 405;
int n, s[MAXN], t[MAXN], tmp[MAXN], m;
int f[MAXN][MAXN], p[MAXN][MAXN], ans[MAXN][MAXN], g[MAXN][MAXN];
int main()
{
scanf("%d", &n);
for (register int i = 1; i <= n; ++i)
{
scanf("%d%d", &s[i], &t[i]);
t[i] += s[i];
tmp[++m] = s[i], tmp[++m] = t[i];
}
sort(tmp + 1, tmp + 1 + m);
m = unique(tmp + 1, tmp + 1 + m) - tmp - 1;
for (register int i = 1; i <= n; ++i)
{
s[i] = lower_bound(tmp + 1, tmp + 1 + m, s[i]) - tmp;
t[i] = lower_bound(tmp + 1, tmp + 1 + m, t[i]) - tmp;
for (register int l = 1; l <= s[i]; ++l)
for (register int r = t[i]; r <= m; ++r)
++p[l][r];
}
for (register int i = 1; i <= m; ++i)
for (register int j = 1; j <= n; ++j)
f[i][j] = g[i][j] = -0x3f3f3f3f;
for (register int k = 1; k <= m; ++k)
for (register int i = 0; i <= p[1][k]; ++i)
for (register int d = 1; d <= k; ++d)
{
f[k][i] = max(f[k][i], f[d][i] + p[d][k]);
if (i >= p[d][k]) f[k][i] = max(f[k][i], f[d][i-p[d][k]]);
}
for (register int k = m; k >= 1; --k)
for (register int i = 0; i <= p[k][m]; ++i)
for (register int d = k; d <= m; ++d)
{
g[k][i] = max(g[k][i], g[d][i] + p[k][d]);
if (i >= p[k][d]) g[k][i] = max(g[k][i], g[d][i-p[k][d]]);
}
for (register int i = 1; i <= m; ++i)
for (register int j = i + 1; j <= m; ++j)
for (register int fa, c, x = 0, y = n; x <= n; ++x)
{
fa = min(x + p[i][j] + y, f[i][x] + g[j][y]);
while (y)
{
c = min(x + p[i][j] + y - 1, f[i][x] + g[j][y - 1]);
if (fa <= c) fa = c, --y;
else break;
}
ans[i][j] = max(ans[i][j], min(x + p[i][j] + y, f[i][x] + g[j][y]));
}
int Ans = 0;
for (register int i = 1; i <= n; ++i) Ans = max(Ans, min(f[m][i], i));
printf("%d\n", Ans);
for (register int k = 1; k <= n; ++k)
{
Ans = 0;
for (register int i = 1; i <= s[k]; ++i)
for (register int j = m; j >= t[k]; --j)
Ans = max(Ans, ans[i][j]);
printf("%d\n", Ans);
}
return 0;
}