枚举,二分,前缀和
前缀和
1200
题意
\(N \le 2 \times 10^5\)
(奇数)个小朋友,第 \(i\) 身高为 \(H_i\) ,老师的身高可以变换,共有\(M \le 2 \times 10^5\) 种形态,第 \(i\) 种身高为 \(W_i\) .
现在小朋友和老师一起做游戏,两个人一组。配对花费为 \[
\sum_{i=1}^{(N+1)/2}|x_i-y_i|
\] \(x_i,y_i\) 是第 \(i\) 组两个人的身高
求最小的的配对花费。
分析
老师必定要匹配一个小朋友。那么可以枚举小朋友和老师匹配,剩下的小朋友自己匹配。自己匹配的最优解一定是排序后两个相邻的匹配。小朋友和老师匹配,就是老师选一个和小朋友最接近的升高来匹配。这个可以排序后用二分
\(O(logM)\)
实现。小朋友两两匹配可以用前缀和预处理好, 枚举的时候 \(O(1)\) 查询.
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
   | #include <bits/stdc++.h> using namespace std; #define sz(x) ((int) (x).size()) typedef pair<int, int> pii; typedef long long ll; const int maxn = 5e5 + 5; template<class T> void print(T & x){ cout << x; } template<class T,class U> void print(pair<T,U> & p){cout << "("; print(p.first); cout << ", "; print(p.second); cout << ")"; } template<class T> void print(vector<T> & v) {     cout << "{";     if (sz(v)) print(v[0]);     for (int i = 1; i < sz(v); i++) cout << ", ", print(v[i]);     cout << "}\n"; }
 
  int n, m; ll H[maxn], W[maxn]; ll suml[maxn], sumr[maxn];
  ll getmin(ll x) {     auto pos = lower_bound(W+1, W+m+1, x) - W;     ll ret = abs(x - W[pos]);     if (pos > 1) ret = min(ret, abs(x - W[pos-1]));     return ret; }
  int main() {     ios_base::sync_with_stdio(0);     cin.tie(0);     cin >> n >> m;     for (int i = 1;i <= n; ++i) {         cin >> H[i];        }     for (int i = 1;i <= m; ++i) {         cin >> W[i];     }     sort(H+1, H+n+1);     sort(W+1, W+m+1);     for (int i = 1;i <= n; ++i) {          if (i % 2 == 0) suml[i] = abs(H[i] - H[i-1]) + suml[i-2];     }     for (int i = n;i >= 1; --i) {          if (i % 2 == 0) sumr[i] = abs(H[i] - H[i+1]) + sumr[i+2];     }     ll ans = 1e18;     for (int i = 1;i <= n; ++i) {         if (i % 2 == 0)              ans = min(ans, suml[i-2] + sumr[i+2] + abs(H[i-1] - H[i+1]) + getmin(H[i]));         else             ans = min(ans, suml[i-1] + sumr[i+1] + getmin(H[i]));     }     cout << ans << endl;     return 0; }
   | 
 
枚举
题意
一个括号匹配序列只有 () 和 [] ,
他们不能交叉比如这样不行, ([)]也不能缺少匹配
[() . 现在有两个数组 \(A,
B\) ,它的长度和某个括号匹配序列一致。在每个对应位置,如果是
() 就选择 \(A\)
数组的元素, 如果是 [] 中的某个就选 \(B\)
的元素。总价值是所有选出元素的和。问选怎样的括号匹配序列才能使价值最大化
分析
首先,我们发现每个括号内的元素个数一定是偶数,因为如果是奇数的话就不能匹配。然后我们反过来想,如果我选出的元素都满足这个条件:相同数组的数之间的数的个数是偶数,是不是一定可以写出一个合法的序列。然后发现是可以的。这个条件也就是奇数位置的数只能匹配偶数的数(否则之间的数就不是偶数了)。假设我们全部选择
\(B\) ,然后我们一步步优化。我们把 \(A_i-B_i\)
存在数组里面。考虑贪心:我们每次从奇数位置选一个最大的,偶数位置选最大的一对。每次更新一下答案。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
   | #include <bits/stdc++.h> using namespace std; #define sz(x) ((int) (x).size()) typedef pair<int, int> pii; typedef long long ll; const int maxn = 5e5 + 5; template<class T> void print(T & x){ cout << x; } template<class T,class U> void print(pair<T,U> & p){cout << "("; print(p.first); cout << ", "; print(p.second); cout << ")"; } template<class T> void print(vector<T> & v) {     cout << "{";     if (sz(v)) print(v[0]);     for (int i = 1; i < sz(v); i++) cout << ", ", print(v[i]);     cout << "}\n"; }
 
  ll n, m, A[maxn], B[maxn];
  int main() {     ios_base::sync_with_stdio(0);     cin.tie(0);     cin >> n;     for (int i = 1;i <= n; ++i) {         cin >> A[i];     }     ll sum = 0;     vector<ll> odd, even;          for (int i = 1;i <= n; ++i) {         cin >> B[i];         sum += B[i];         if (i % 2) odd.push_back(A[i] - B[i]);         else even.push_back(A[i] - B[i]);     }     ll ans = sum;     sort(odd.begin(), odd.end(), greater<>());     sort(even.begin(), even.end(), greater<>());     for (int i = 0;i < sz(odd); ++i) {         sum += odd[i] + even[i];         ans = max(sum, ans);     }     cout << ans << endl;     return 0; }
   | 
 
按 \(x\) 排序,二维枚举
题意
二维平面里面,有 \(n\) 个人,\(m\) 个灯塔。灯塔 \((c_i,d_i)\) 照亮的范围是 $(x, y) $ 如果
\(x \le c_i\) 且 \(y \le d_i\) .
所有人必须一起移动,它们在一步内可以左右或者上下移动。求最小的步数,使得所有人远离灯塔的范围。
分析
如果把题目抽象一下。\((a_i,b_i)\) 和
\((c_i,d_i)\) 是人和灯塔。那么要求一个
\((x,y)\)
使得对于所有人来说都不被所有灯塔照亮。也就是对于每个人和每个灯塔 \(a_i+x > c_j\) 或者 \(b_i +y > c_j\)
至少一个成立。那么我们枚举 \((i,j)\)
,把所有的对都枚举出来,并且只留下不满足的二元对。然后把这些对按 \(x\) 排序。那么我们可以枚举 \(x\) , 假设我们向上移动 \(x\), 那么 \(y\) 就是所有比 \(x\) 大的对中的最大值。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
   | #include <bits/stdc++.h> using namespace std; #define _ <<" "<< #define sz(x) ((int) (x).size()) typedef pair<int, int> pii; typedef long long ll; const int maxn = 5e5 + 5; template<class T> void print(T & x){ cout << x; } template<class T,class U> void print(pair<T,U> & p){cout << "("; print(p.first); cout << ", "; print(p.second); cout << ")"; } template<class T> void print(vector<T> & v) {     cout << "{";     if (sz(v)) print(v[0]);     for (int i = 1; i < sz(v); i++) cout << ", ", print(v[i]);     cout << "}\n"; }
 
  int n, m; int a[maxn], b[maxn],c[maxn], d[maxn];
  int main() {     ios_base::sync_with_stdio(0);     cin.tie(0);     cin >> n >> m;     for (int i = 1;i <= n; ++i) {         cin >> a[i] >> b[i];     }        for (int i = 1;i <= m; ++i) {         cin >> c[i] >> d[i];     }     vector<pii> A;     for (int i = 1;i <= n; ++i) {         for (int j = 1;j <= m; ++j) {             if (c[j] - a[i] >= 0 && d[j] - b[i] >= 0 )                 A.push_back({c[j] + 1 - a[i], d[j] + 1 - b[i]});         }     }     sort(A.begin(), A.end());     ll mx = 0;     ll ans = 1e18;     for (int i = sz(A)-1; i >= 0; --i) {         auto [x, y] = A[i];           ll cur = x + mx;         ans = min(ans, cur);           mx = max(mx, (ll)y);     }     ans = min(ans, mx);     if (ans > 1e9) cout << 0 << endl;     else cout << ans << endl;     return 0; }
   | 
 
枚举+二分
题意
分析
因为音符的顺序和琴弦的顺序没有影响,所以我们把音符和弦排序。枚举一个位置
\(minx\)
作为最右边的音符。那么其他的音符摆放的位置要不浪费。不浪费也就是说,尽可能往右边摆放。那么一个音符如果往下挪一行就越过最右边了,那么它就应该摆在上一行。应该是摆在这一行刚好在
\(minx\)
左边一点。那么这个值必定是印象最大值的。因为根据排序,之前枚举的值肯定比当前的值小,那么它摆在同一行的上面肯定在当前的值的右边。所以我们对于每个琴弦,二分查找一下刚好不去下一行中最大的那个音符
\(B_i\)。若这一行琴弦的值是 \(a_j\) 那么就是 \(B_i-a_j <= minx\) 且 $B_i-a_{j+1} >
minx $ 也就是 \(minx + a_{j+1}\)
的前驱。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
   | #include <bits/stdc++.h> using namespace std; #define sz(x) ((int) (x).size()) typedef pair<int, int> pii; typedef long long ll; const int maxn = 5e5 + 5; template<class T> void print(T & x){ cout << x; } template<class T,class U> void print(pair<T,U> & p){cout << "("; print(p.first); cout << ", "; print(p.second); cout << ")"; } template<class T> void print(vector<T> & v) {     cout << "{";     if (sz(v)) print(v[0]);     for (int i = 1; i < sz(v); i++) cout << ", ", print(v[i]);     cout << "}\n"; }
  int n, m; int A[7], B[maxn]; vector<int> a;
  int solve(int minx) {     vector<pair<int, int>> lr;      int cur = 0;     for (int k = 1;k < sz(a); ++k){         int pos = lower_bound(B+1, B+n+1, minx + a[k]) - B - 1;          cur = max(cur, B[pos] - a[k-1]);     }     for (int k = sz(a)-1;k >= 0; --k) {         if (B[n] - a[k] >= minx) {             cur = max(cur, B[n] - a[k]);             break;         }     }     return cur; }
  int main() {     ios_base::sync_with_stdio(0);     cin.tie(0);     for (int i = 1;i <= 6; ++i) {         cin >> A[i];     }     sort(A+1, A+6+1);     for (int i = 1;i <= 6; ++i) {         if (a.empty() || A[i] != a.back()) a.push_back(A[i]);     }     cin >> n;     for (int i = 1;i <= n; ++i) {         cin >> B[i];     }     sort(B+1, B+n+1);     int ans = INT_MAX;     for (int i = 0;i < sz(a); ++i) {         for (int j = 1;j <= n; ++j) {             int minx = B[j] - a[i];             if (B[1] - a[0] < minx) continue;             int cur = solve(minx);             ans = min(cur - minx, ans);         }     }     cout << ans << endl;     return 0; }
   |