我們發現挖掘乙個點所需要的方塊是乙個向上的倒三角, 我們設這個倒三角在 \(y = -1\) 上的左右端點分別為 \(l, r\)
不難發現, 乙個 \([l, r]\) 區間僅對應乙個點, 也就是說, 每個點所需要的倒三角都是不同的
如果乙個區間被另外乙個區間完全覆蓋, 那麼選了大的那個區間, 小區間的貢獻就可以直接加上了, 而不需要算選小區間的代價
同理, 若兩個區間相交, 那麼選這兩個區間的利潤就是他們的貢獻減去他們的代價再加上重複部分的代價
因為這一部分被多減了一次
但是從這裡又會發現乙個問題, 若兩個區間同時完全覆蓋乙個小區間, 並且這個兩個區間重疊的區間也完全覆蓋這個小區間
那這個小區間的貢獻又會被多算一次, 要減掉
我們把區間按照右端點排序, 並預處理出每個區間完全覆蓋的區間, 將貢獻加上去
設 \(f[i]\) 代表前 \(i\) 個區間, \(i\) 必選的最大利潤
考慮列舉 \(j\)
若 \(j\) 區間被 \(i\) 區間完全覆蓋, 貢獻已經算進 \(i\) 區間了, 不從這種 \(j\) 轉移
若 \(j\) 區間與 \(i\) 區間有交, 減去交的這一部分的重複貢獻, 加上交的這一部分的重複代價
若 \(j\) 區間與 \(i\) 區間無交, 直接加上 \(j\) 的利潤即可
這樣子是 \(o(n^3)\) 的
考慮優化算重複貢獻的部分, 發現右端點單調不降, 拿個指標維護一下即可
#include #include #include #include using namespace std;
int n, f[1005], ans, cnt, r;
struct node
} p[1005];
template < typename t >
inline t read()
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * w;
}int calc(int r, int l)
int main()
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
if(p[j].l >= p[i].l && p[j].r <= p[i].r)
p[i].v2 += p[j].v1;
sort(p + 1, p + n + 1);
f[0] = 0;
for(int i = 1; i <= n; i++)
tmp = calc(p[j].r, p[i].l);
f[i] = max(f[i], f[j] + p[i].v2 - p[i].c + tmp - cnt);
}if(p[j].r < p[i].l)
f[i] = max(f[i], f[j] + p[i].v2 - p[i].c);
} }for(int i = 0; i <= n; i++)
ans = max(ans, f[i]);
printf("%d\n", ans);
return 0; }/*
51 -1 2
0 -1 2
4 -1 1
3 -1 2
2 -1 2
*/
題解 JSOI2010 排名
題目很簡單,用優先佇列維護拓撲序即可 但是他要我們求的東西比較詭異,導致貪心的策略會不同 對於第一問,我們一般的思路是直接維護正的拓撲序,每次選最小的更新 但這樣不一定是最優的,因為選擇了當前最小的可能使得更小的拓撲序排在後面,這樣字典序就不一定是最小的了 所以我們維護反的拓撲序,每次選最大的更新 ...
JSOI2010 快取交換
考慮乙個貪心 就是每次我們都選擇佇列裡面之後最晚加入的元素彈出。維護乙個nxt陣列就行了。特判一下之後不會再加入的元素。如下 include include include include include include define maxn 200010 using namespace std ...
JSOI2010 快取交換
當遇到需要將主存單元加進cache的時候,就看cache裡是否滿了,滿了的話,就刪除離最靠後的那乙個,這樣一定最優。但是網上部落格的 太長了。其實只需要判斷一下當前的優先佇列的top是否仍在cache中即可。重題 poi2005 sam toy cars,ac數 2 include include ...