[題解]P1311 [NOIP2011 提高組] 選擇客棧

Sinktank發表於2024-10-17

P1311 [NOIP2011 提高組] 選擇客棧
P6032 選擇客棧 加強版

只要\([l,r]\)區間之記憶體在一個\(i\)使得\(w[i]\le p\),這個區間就是符合條件的。

所以我們遍歷每一個元素\(i\),根據貪心的思想我們維護\([1,i]\)區間內滿足\(w[i]\le p\)的最大\(i\),記為\(mp\)

對於每個元素\(i\),尋找\([1,mp]\)之間顏色和\(i\)相同的元素(不能是\(i\)本身)個數,累計答案即可。

可以用陣列維護\([1,i]\)中各個顏色的位置,二分求解。時間複雜度\(O(n\log n)\)

點選檢視程式碼
#include<bits/stdc++.h>
#define N 2000010
#define int long long
using namespace std;
int n,k,p,ans,mp;
vector<int> pos[N];
signed main(){
	cin>>n>>k>>p;
	for(int i=1;i<=n;i++){
		int k,v;
		cin>>k>>v;
		if(v<=p) mp=i;
		if(!pos[k].empty()) ans+=upper_bound(pos[k].begin(),pos[k].end(),mp)-pos[k].begin();
		pos[k].emplace_back(i);
	}
	cout<<ans<<"\n"; 
	return 0; 
}

容易發現,對\(pos[k]\)進行二分,得到的答案一定是單調的,所以放棄二分,直接動態維護每個當前顏色的答案即可。

點選檢視程式碼
#include<bits/stdc++.h>
#define N 2000010
#define int long long
using namespace std;
int n,k,p,ans,mp,res[N];
vector<int> pos[N];
signed main(){
	memset(res,-1,sizeof res);
	cin>>n>>k>>p;
	for(int i=1;i<=n;i++){
		int k,v;
		cin>>k>>v;
		if(v<=p) mp=i;
		if(!pos[k].empty()){
			while(res[k]+1<pos[k].size()&&pos[k][res[k]+1]<=mp) res[k]++;
			ans+=res[k]+1;
		}
		pos[k].emplace_back(i);
	}
	cout<<ans<<"\n"; 
	return 0; 
}

相關文章