P8125 [BalticOI 2021 Day2] The short shank 題解

Creeper_l發表於2024-06-05

首先會發現若 \(t_i <= T\) 的話那麼他最終一定會造反。

我們只考慮 \(t_i >T\) 的情況。設 \(lst_i\) 表示 \(i\) 左邊第一個可以影響(使他造反)到 \(i\) 的位置,那麼我們一定要在 \([lst_i,i]\) 這個區間中的某一個位置放上床墊才能使 \(i\) 不造反。

這樣有一個 \(O(nd)\) 的 dp,但是複雜度顯然會炸。考慮挖掘一些性質,會發現所有區間都是包含或者不交的。證明也比較簡單,若兩個區間有交,即 \(i<j,lsti \le lst_j \le i\),那麼 \(lst_j\) 這個位置也一定能影響到 \(i\),矛盾。所以任意兩個區間都不交叉。

那麼我們按照區間的包含關係建圖,就變成了一個森林。考慮轉化題意,若在葉子節點的區間中放一個床墊,那麼它到根節點的路徑上的所有點都不會造反了。所以題意轉化為求 \(d\) 條不相交的鏈,使得這些鏈的總長度最大。這個問題直接長鏈剖分,然後取前 \(d\) 長的鏈即可。

\(lst\) 陣列以及連邊都可以用單調棧維護,最終求前 \(d\) 長鏈可以用優先佇列維護,時間複雜度 \(O(n+d \log n)\)

關於取前 \(d\) 長的鏈的簡單證明:對於兩條不是最長鏈的鏈,我們都可以把它換成最長鏈加另外一條鏈,這樣總長度一定不劣(畫個圖更容易理解)。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e6 + 10;
int n,d,T,t[MAXN],lst[MAXN],ans,fa[MAXN];
int head[MAXN],cnt,depth[MAXN],son[MAXN],mx[MAXN];
struct Node {int u,v,nxt;}e[MAXN << 1];
void Add(int u,int v) {e[++cnt] = {u,v,head[u]};head[u] = cnt;}
priority_queue <int,vector <int>,less <int> > q;
void dfs(int u,int father) {
	depth[u] = depth[father] + 1,mx[u] = depth[u];
	for(int i = head[u]; ~ i;i = e[i].nxt) {
		int now = e[i].v;
		if(now == father) continue;
		dfs(now,u),mx[u] = max(mx[u],mx[now]);
		if(mx[now] > mx[son[u]]) son[u] = now;
	}
} void sdfs(int u,int father,int len) {
	if(son[u] == 0) return q.push(len);
	sdfs(son[u],u,len + 1);
	for(int i = head[u]; ~ i;i = e[i].nxt) 
		if(e[i].v != father && e[i].v != son[u]) sdfs(e[i].v,u,1);
} stack <int> s;
signed main() {
	memset(head,-1,sizeof head);
	cin >> n >> d >> T;
	for(int i = 1;i <= n;i++) cin >> t[i];
	for(int i = 1;i <= n;i++) 
		if(t[i] > T) {
			while(s.size() && t[s.top()] + i - s.top() > T) s.pop(); 
			if(s.size()) lst[i] = s.top();
		} else {
			while(s.size() && t[s.top()] + i - s.top() >= t[i]) s.pop(); 
			s.push(i);
		}
	while(!s.empty()) s.pop();
	for(int i = n;i >= 1;i--) {
		if(lst[i] == 0) continue;
		while(!s.empty() && lst[s.top()] > lst[i]) s.pop();
		if(!s.empty()) Add(s.top(),i),fa[i] = s.top();
		s.push(i); 
	} for(int i = 1;i <= n;i++) {
		if(t[i] <= T) continue;
		if(lst[i] == 0) ans++;
		else if(fa[i] == 0) dfs(i,0),sdfs(i,0,1);
	} while(d > 0 && !q.empty()) 
		ans += q.top(),q.pop(),d--; 
	cout << n - ans;
	return 0;
} 

相關文章