首先會發現若 \(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;
}