1. 程式人生 > 其它 >【洛谷P7416】No Time to Dry P

【洛谷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\)

的未被塗色的柵欄可以按如下方式上色:

\[0000 \to 1110 \to 1122 \to 1332 \]

不幸的是,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;
}