比如今年 S-T1,去年 NOIp-T1,讲真我挺害怕这种贪心的,所以记录一些偏向思维/技巧的贪心题。
受 Codeforces 的启发,尝试这样一种新的题解风格。
用这种风格,大概是为了让自己搞懂“为什么想到这样转化”,对考场思维起一点微弱的引导作用(?)。
真心希望不要再 T1 做不出来了(哭)
1. AGC048B Bracket Score
Hint 1
转化一下题意,考虑一个只有小括号的串串,你要将其中一部分小括号替换成中括号。替换第 \(i\) 个小括号对答案的贡献是多少?
Hint 2
中括号应该怎么放才能使整个串串合法?Hint 3
左括号还是右括号并不重要。Hint 4
一种替换方案合法的充要条件是什么?Sol
先令答案为 $\sum a_i$。我们发现,一种替换方案合法,当且仅当每对中括号的下标异奇偶。
为此,将奇数和偶数位的 \(b_i-a_i\) 分别存起来,从大到小排序。
每次我们取出两个序列的最大值 \(x,y\),若 \(x+y>0\) 则累入答案,相当于我们将两个小括号换成了一对中括号。
代码实现用的是优先队列。
总时间 \(O(n\log n)\)。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n,a[N],b[N],ans;
priority_queue<int> q1,q2;
signed main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);cin>>n;for(int i=1;i<=n;i++) cin>>a[i],ans+=a[i];for(int i=1;i<=n;i++){cin>>b[i];if(i&1) q1.push(b[i]-a[i]);else q2.push(b[i]-a[i]);}while(!q1.empty()&&!q2.empty()){int x=q1.top(),y=q2.top();if(x+y<0) break;q1.pop(),q2.pop();ans+=x+y;}cout<<ans<<"\n";return 0;
}
2. AGC018C Coins
Hint 1
考虑沿用上题的转化。先令所有人币种一样,再进行调整。Hint 2
考虑一个人的 $2$ 种调整策略对答案的贡献分别是多少?Hint 3
尝试用交换法来搞出一个贪心策略?Sol
先令答案为 $\sum a_i$,即所有人都选金币。然后,我们使用 \(y\) 个银币、\(z\) 个铜币替换掉一部分金币。
改换银币、铜币对答案的贡献是 \((d,e)=(b-a,c-a)\)。
假设“\(i\) 选 \(d\),\(j\) 选 \(e\)”优于“\(i\) 选 \(e\),\(j\) 选 \(d\)”,也即 \(d_i+e_j\ge e_i+d_j\),移项得 \(d_i-e_i\ge d_j-e_j\)。
按 \(d-e\) 从大到小排序后,一定存在一个最优解,满足选 \(d\) 的全在选 \(e\) 的左边。
考虑枚举这个分段点,并预处理出它左边选 \(y\) 个 \(d\) 的最大贡献,以及它右边选 \(z\) 个 \(e\) 的最大贡献。
预处理可以用堆。计算每个前缀选 \(d\) 的最大贡献,我们开一个小根堆,大小超过 \(y\) 后弹出堆顶即可。\(e\) 同理。
总时间 \(O(n\log n)\)。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n,x,y,z,ts,lmx[N],rmx[N],ans=-1e18;
struct Nd{int d,e;}s[N];
priority_queue<int,vector<int>,greater<int>> q;
signed main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);cin>>x>>y>>z,n=x+y+z;for(int i=1,a,b,c;i<=n;i++){cin>>a>>b>>c;ts+=a,s[i]={b-a,c-a};}sort(s+1,s+1+n,[](Nd i,Nd j){return i.d-i.e>j.d-j.e;});for(int i=1;i<=n;i++){lmx[i]=lmx[i-1]+s[i].d;q.push(s[i].d);if(q.size()>y) lmx[i]-=q.top(),q.pop();}while(!q.empty()) q.pop();for(int i=n;i;i--){rmx[i]=rmx[i+1]+s[i].e;q.push(s[i].e);if(q.size()>z) rmx[i]-=q.top(),q.pop();}for(int i=y;i<=n-z;i++) ans=max(ans,lmx[i]+rmx[i+1]);cout<<ts+ans<<"\n";return 0;
}
正在找题……如果有任何推荐请在评论踢我。