【洛谷P7416】No Time to Dry P
題目
題目連結:https://www.luogu.com.cn/problem/P7416
Bessie 最近收到了一套顏料,她想要給她的牧草地一端的柵欄上色。柵欄由 \(N\) 個 \(1\) 米長的小段組成。Bessie 可以使用 \(N\) 種不同的顏色,她將這些顏色由淺到深用 \(1\) 到 \(N\) 標號(\(1\) 是很淺的顏色,\(N\) 是很深的顏色)。從而她可以用一個長為 \(N\) 的整數陣列來描述她想要給柵欄的每一小段塗上的顏色。
初始時,所有柵欄小段均未被上色。Bessie 一筆可以給任意連續若干小段塗上同一種顏色,只要她不會在較深的顏色之上塗上較淺的顏色(她只能用較深的顏色覆蓋較淺的顏色)。
例如,一段長為 \(4\)
不幸的是,Bessie 沒有足夠的時間等待顏料變幹。所以,Bessie 認為她可能需要放棄為柵欄上某些小段上色!現在,她正在考慮 \(Q\) 個候選的區間,每個區間用滿足 \(1 \leq a \leq b \leq N\) 的兩個整數 \((a,b)\) 表示,為需要上色的小段 \(a \ldots b\) 的兩端點位置。
對於每個候選區間,將所有區間內的柵欄小段都塗上所希望的顏色,並且區間外的柵欄小段均不塗色,最少需要塗多少筆?注意在這個過程中 Bessie 並沒有真正進行任何的塗色,所以對於每個候選區間的回答是獨立的。
\(N,Q\leq 2\times 10^5\)
思路
顯然最優的塗色方案就是從淺往深塗。也就是說,如果位置 \(i\),上一個與他顏色相同的位置是 \(j\),且 \(j\sim i\) 直接存在顏色淺於 \(a_i\) 的,那麼就需要多塗一次。
把詢問按照右端點離線,依次加入顏色,每新加入一個顏色就找到上一個和他相同顏色的位置 \(p\),以及上一個顏色小於他的位置 \(q\),如果 \(p<q\),那麼當詢問的區間左端點不超過 \(p\) 時,就會有 \(1\) 的貢獻。
維護兩個樹狀陣列就好了。
時間複雜度 \(O(n\log n)\)。
程式碼
#include <bits/stdc++.h> using namespace std; const int N=200010; int n,Q,a[N],last[N],ans[N]; struct node { int l,r,id; }b[N]; bool cmp(node x,node y) { return x.r<y.r; } struct BIT { int typ,c[N]; void add(int x,int v) { for (int i=x;i<=n;i+=i&-i) c[i]=typ ? (c[i]+v) : max(c[i],v); } int query(int x) { int ans=0; for (int i=x;i;i-=i&-i) ans=typ ? (ans+c[i]) : max(ans,c[i]); return ans; } }bit1,bit2; int main() { scanf("%d%d",&n,&Q); for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=Q;i++) { scanf("%d%d",&b[i].l,&b[i].r); b[i].id=i; } sort(b+1,b+1+Q,cmp); bit1.typ=1; for (int i=1,j=1;i<=n;i++) { int p=last[a[i]],q=bit2.query(a[i]-1); if (p<q && p) bit1.add(p,1); if (p) bit1.add(p,-1); last[a[i]]=i; bit1.add(i,1); bit2.add(a[i],i); for (;j<=Q && b[j].r<=i;j++) ans[b[j].id]=bit1.query(i)-bit1.query(b[j].l-1); } for (int i=1;i<=Q;i++) printf("%d\n",ans[i]); return 0; }