1. 程式人生 > 其它 >【做題記錄】CF746F Music in Car

【做題記錄】CF746F Music in Car

CF746F Music in Car

Problem

CF746F

題目大意:

\(k\) 的時間,\(n\) 首歌,聽每首歌的時間不一定相同,要求按順序聽一段連續的歌,其中有 \(w\) 首可以只聽一半(向上取整)。每首歌有一個快樂值,聽第 \(i\) 首歌即可獲得 \(a_i\) 的快樂值(不論聽整首還是半首),問能獲得的最大的快樂值是多少。

Solution

首先有一個很明顯的事,就是對於每一個確定的區間左邊界 \(l\),一定要使 \(r\) 儘可能的大,也就是確定做斷電後,只要能擴充套件區間就擴充套件區間。然後看資料範圍,\(1\le n \le 2 \times 10^5\),可以想到要用 \(O(n\log n)\)

或者 \(O(n)\) 的演算法。
於是我們考慮用 two-pointers 解決這題。
這時還需要用到一個貪心,就是對於目前 two-pointers 找到的一個區間 \([l,r]\),我們必然讓時間最大的 \(w\) 首歌只聽一半。

具體實現時,我們可以用 STL 中的 multiset<> 來儲存歌曲,這裡不能用 set<> 是因為兩首歌的時間可能一樣。
我們用兩個 multiset<> \(s_1,s_2\) 分別儲存聽一半的歌的原本的時間和聽整首歌的時間。
對於每次將要進入區間的一個數,我們進行分類討論:

  • 若只有不到 \(w\) 首歌在 \(s1\)
    中且剩餘時間足夠聽半首歌,彈入 \(s_1\)
  • 否則,若 \(s_1\) 中原時間最小的數比當前數要小,則把當前數放入 \(s_1\),將那個數放入 \(s_2\)\(s_1\) 存原時間的好處就在這裡,如果存半首的時間,將無法確定原時間,把元素放入 \(s_2\) 時就會出問題。另外,這裡還要判一下放進去調整後的時間夠不夠用。
  • 否則,若剩餘時間夠聽整首歌,則把當前數放入 \(s_2\)
  • 否則,說明這個區間已經最大了,放不進去了,就需要彈出左端點。這裡也需要分類討論:
    • \(s_2\) 為空,左端點一定在 \(s_1\) 中,刪除即可。
    • 否則,若 \(s_1\) 最小的數比左端點的時間大,那麼左端點在 \(_2\)
      中,刪除即可。
    • 否則,左端點在 \(s_1\) 中,把左端點刪除後, \(s_1\) 不滿而 \(s_2\) 有數,把 \(s_2\) 中最大的數放入 \(s_1\)

於是就可以完成此題了!

Code

#include<bits/stdc++.h>
using namespace std;
int n,w,k,a[200005],t[200005];
int T,res,ans;
multiset<int>s1,s2;
int main()
{
	scanf("%d%d%d",&n,&w,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) scanf("%d",&t[i]);
	int l=1,r=0;
	while(r<n)
	{
		r++;
		int tmp=*s1.begin();
		if((int)s1.size()<w&&T+(t[r]+1)/2<=k)
			s1.insert(t[r]),T+=(t[r]+1)/2,res+=a[r];
		else if(t[r]>tmp&&T-(tmp+1)/2+(t[r]+1)/2+tmp<=k)
		{
			s1.erase(s1.find(tmp));
			s1.insert(t[r]);
			s2.insert(tmp);
			T+=(t[r]+1)/2-(tmp+1)/2+tmp;
			res+=a[r];
		}
		else if(T+t[r]<=k) s2.insert(t[r]),T+=t[r],res+=a[r];
		else
		{
			r--;
			if(s1.empty()){r++;continue;}
			else if(s2.empty()) s1.erase(s1.find(t[l])),T-=(t[l]+1)/2;
			else if(t[l]<tmp) s2.erase(s2.find(t[l])),T-=t[l];
			else
			{
				int ttmp=*--s2.end();
				s1.erase(s1.find(t[l]));
				s1.insert(ttmp);
				s2.erase(s2.find(ttmp));
				T=T-(t[l]+1)/2-ttmp+(ttmp+1)/2;
			}
			res-=a[l];
			l++;
		}
		ans=max(ans,res);
	}
	printf("%d\n",ans);
	return 0;
}