Codeforces Round 894 (Div. 3) E. Kolya and Movie Theatre

99 阅读3分钟

今天好开心wwwwww,印象中应该是第一次自己开出了16001600的题,显然我太菜了。。。

链接: Problem - E - Codeforces

当然写之前我也不知道这是16001600的题,我就感觉很有意思。 然后接下来我将记录下写这道题的全部心路历程。(我很菜,大佬轻点骂)。

分析

首先,我看到这个题第一想法就是dpdp,之前打多了,发现很多这种难度的思维题都是dpdp,我当时考虑的是dp[i]dp[i]记录走到第ii天的最大值,但是很快发现,被暴打了,首先就算不考虑mm天,光这个dd时间复杂度就要O(n2)O(n^2)这不是扯淡吗?,然后加上一个mm,直接O(nm2)O(nm^2)你fp呢,这复杂度过nm呢,于是排除了这个想法,然后又想到了空间换时间dp[i][j]dp[i][j]表示走到第ii天时候选了jj天的最大值,然后显然不行,直接内存超限了。然后就是还想到了状态dp[i][k]dp[i][k]kk只取0101分别表示拿还是不拿,但是这样也仅仅把时间复杂度降到了O(nm)O(nm),还是过不去,正当我想去看题解的时候,突然———————————————————————————————————————— 我发现了一个点,也就是这个点让我突破了这个题,就是我们其实对于dd的考虑只需要考虑什么呢?只需要考虑我们最后一次取数在第几天,因为无论我们前面怎么取(就算什么都不取),我们最后也是在要减去kdkd,其中kk为最后取的那一天。于是其实我们要比较的就是最后在哪一天取的时候我们再减去kdkd得到的值最大即可,于是我们很显然至少要把序列枚举一遍也就是O(n)O(n),而dpdp的话显然不行,因为我们还有mm维,所以这个时候我就发现这个题其实是个贪心,然后这时我就在想,应该怎么考虑这mm个数呢,或者是m<m个数?我就想到了可以用一个大根堆来维护,首先如果这个数比00小,我们显然不要放进去,因为不选都是00,也比他大,然后对于我们遍历到的每个ii,如果堆的大小<m<m的话,我们就把这个数塞进去,如果=m=m的时候,我们就看堆顶元素,要是这个a[i]>heap.top()a[i]>heap.top(),那么我们就执行poppop操作,然后让这个数入堆,再在这个过程中维护一个resres来记录每次的最大值,然后每次遍历结束,我们更新一下ansans即可,记好开longlonglong long,然后这个题我们就完美的解决啦!然后一发过样例一发过了这题很快的啊!!!时间复杂度O(nlogm)O(nlogm)

代码

#include <bits/stdc++.h>


#define ll long long

#define x first
#define y second

using namespace std;
const int N=200010;
int t;
priority_queue<ll,vector<ll>,greater<ll>> heap;
ll n,m,d;//n天m场电影d差值 
ll a[N];//a[i]表示值 
void solve(){
	while(heap.size()){
		int to=heap.top();
		heap.pop();
	}
	ll ans=0,res=0;
	cin>>n>>m>>d; 
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++){
		if(a[i]>0){
			int si=heap.size();
			if(si<m){
				heap.push(a[i]);
				res+=a[i];
			}
			else if(si==m){
				ll to=heap.top();
				if(a[i]>to){
					heap.pop();
					heap.push(a[i]);
					res+=a[i];
					res-=to;
				}
			}
		}
		ll temp=res-i*d;
		ans=max(ans,temp);
	}
	cout<<ans<<"\n";
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	
	
	cin>>t;
	while(t--) solve();
	return 0; 
}

加油嘞!