1
将式子$\sum_{i=1}^n |a_i - b_i|$抽象成线段, 手玩一下发现更优情况只出现在相离线段
#include<bits/stdc++.h> using namespace std; #define endl "\n" #define int long long typedef long long ll; /* https://www.luogu.com.cn/problem/CF1898D 将式子抽象成线段 $\sum_{i=1}^n |a_i - b_i|$ */ const int N = 2e5 + 10; struct node { int l, r; } Seg[N]; int len[N]; signed main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int T = 1; cin >> T; while(T--) { int n; cin >> n; int sum = 0; for(int i = 1; i <= n; i++) cin >> Seg[i].l; for(int i = 1; i <= n; i++) { cin >> Seg[i].r; len[i] = abs(Seg[i].l - Seg[i].r); sum += len[i]; if(Seg[i].r < Seg[i].l) swap(Seg[i].l, Seg[i].r); } int minr = INT_MAX, maxl = 0; for(int i = 1; i <= n; i++) { auto [l, r] = Seg[i]; if(minr > r) { minr = r; // maxrlen = len[i]; } } for(int i = 1; i <= n; i++) { auto [l, r] = Seg[i]; if(maxl < l) { maxl = l; // maxllen = len[i]; } } // cout << maxrlen << ' ' << maxllen << ' ' << maxl << ' ' << minr << endl; if(maxl >= minr) cout << sum + 2 * (maxl - minr) << endl; else cout << sum << endl; } return 0; }
大体思路对了,但是没想到最小值点可以是1或m,并且我想的是找最大值点
假设已知最小值点为x,最大值点为y,那么对于一个x在的区间同时包含x,y对答案完全没有贡献,不含y的话会使情况更劣,所以我们选择所有不含x的区间,找最大值
感性理解最不容易被覆盖到的点就是起点1和终点m,那么我们只需要分别考虑这两点不包含的情况取max即可
#include<bits/stdc++.h> using namespace std; #define endl "\n" typedef long long ll; const int N = 2e5 + 10; vector<int> vec; array<int, 2> que[N]; int pre[N]; int get_idx1(int x) { return lower_bound(vec.begin(), vec.end(), x) - vec.begin() + 1; } int get_idx2(int x) { return lower_bound(vec.begin(), vec.end(), x + 1) - vec.begin() + 1; } int main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int T = 1; cin >> T; while(T--) { int n, m; cin >> n >> m; vec.clear(); for(int i = 1; i <= n; i++) { int l, r; cin >> l >> r; vec.push_back(l); vec.push_back(r + 1); que[i] = {l, r}; } sort(vec.begin(),vec.end()); vec.erase(unique(vec.begin(), vec.end()), vec.end()); int sz = vec.size(); for(int i = 1; i <= n * 2; i++) pre[i] = 0; for(int i = 1; i <= n; i++) { auto [l, r] = que[i]; if(l == 1) continue; int idl = get_idx1(l), idr = get_idx2(r); pre[idl]++; pre[idr]--; } int maxn = 0; for(int i = 1; i <= n * 2; i++) { pre[i] = pre[i] + pre[i - 1]; if(pre[i] > maxn) maxn = pre[i]; // cout << pre[i] << ' '; } // cout << endl; for(int i = 1; i <= n * 2; i++) pre[i] = 0; for(int i = 1; i <= n; i++) { auto [l, r] = que[i]; if(r == m) continue; int idl = get_idx1(l), idr = get_idx2(r); pre[idl]++; pre[idr]--; } for(int i = 1; i <= n * 2; i++) { pre[i] = pre[i] + pre[i - 1]; maxn = max(maxn, pre[i]); } // cout << maxn << ' ' << minn << endl; cout << maxn << endl; } return 0; }
要有 i + 1 个 i 的阶乘才能转换成一个 i + 1 的阶乘,模拟一下转换过程就行
#include<bits/stdc++.h> using namespace std; #define endl "\n" #define int long long typedef long long ll; const int N = 5e5 + 10; int a[N], f[N]; signed main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int n, x; cin >> n >> x; for(int i = 1; i <= n; i++) { cin >> a[i]; f[a[i]]++; } bool ok1 = true, ok2 = false; for(int i = 1; i <= N - 5; i++) { f[i + 1] += f[i] / (i + 1); f[i] = f[i] % (i + 1); } for(int i = 1; i < x; i++) { if(f[i]) ok1 = false; } for(int i = x; i <= N - 5; i++) { if(f[i]) ok2 = true; } if(ok1 && ok2) cout << "Yes"; else cout << "No"; return 0; }
转换成前缀和,那么每个0的位置只会影响到后面的位置,检查第 i 个 0 到第 i + 1 个 0 之间众数是多少,就是两个 0 夹着这段的贡献, 我们把众数变成 0
#include<bits/stdc++.h> using namespace std; #define endl "\n" #define int long long typedef long long ll; const int N = 2e5 + 10; int a[N], vis[N], pre[N]; signed main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int T = 1; cin >> T; while(T--) { // cout << "TEST" << endl; int n; cin >> n; vector<int> zero; for(int i = 1; i <= n; i++) { cin >> a[i]; pre[i] = pre[i - 1] + a[i]; if(!a[i]) zero.push_back(i); } int sz = zero.size(), ans = 0; // cout << sz << endl; if(sz) { for(int i = 1; i < zero[0]; i++) { if(!pre[i]) ans++; } for(int i = 0; i < sz - 1; i++) { int now = zero[i], maxn = 0; map<int, int> mp; while(now < zero[i + 1]) { mp[pre[now]]++; maxn = max(maxn, mp[pre[now]]); now++; } // cout << maxn << ' ' << zero[i] << ' ' << zero[i + 1] << endl; ans += maxn; } int maxn = 0; map<int, int> mp; for(int i = zero[sz - 1]; i <= n; i++) { mp[pre[i]]++; maxn = max(maxn, mp[pre[i]]); } ans += maxn; } else { for(int i = 1; i <= n; i++) { if(!pre[i]) ans++; } } cout << ans << endl; } return 0; }
贪心的想, 其中一个串必定为他本身,我们让高位尽可能为 1 ,我们找到最高位的 0 去更高位找与他匹配的串,且此时长度已经定下来了就是 0 出现的位置到最低位的长度
保证数据 01出现概率相等, 那么我们可以知道连续出现一段 0 或 1 的概率非常小, 连续 30 个 1 概率都为 $2^30$ 了,那么我们暴力跑就行了,最高位 1 到最高位 0 差一定是在 30 位内的
#include<bits/stdc++.h> using namespace std; #define endl "\n" typedef long long ll; int main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int n; cin >> n; string s; cin >> s; int p1 = s.find('1'); if (p1 == s.npos) { cout << 0; return 0; } int p2 = s.find('0', p1); string maxn = s; for (int i = p1; i < p2; i++) { string t = s; for (int j = i + n - 1 - p2, k = n - 1; j >= i; j--, k--) { t[k] |= s[j]; } maxn = max(maxn, t); } cout << maxn.substr(maxn.find('1')); return 0; }
Sending a Sequence Over the Network
想到 dp 了,但是没想出来怎么转移
$f_i$表示区间$[1, i]$是否合法
那么假设当前点 i 是表示一段区间长度的数,可以得到以下转移方程
若当前数在区间左边, 那么$[1, i + a_i]$合法的条件是$[1, i - 1]$合法
若当前数在区间右边,那么$[1, i]$合法的条件是$[1, i - a_i - 1]$合法
#include<bits/stdc++.h> using namespace std; #define endl "\n" typedef long long ll; const int N = 2e5 + 10; int a[N], f[N]; int main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int T = 1; cin >> T; while(T--) { int n; cin >> n; for(int i = 1; i <= n; i++) cin >> a[i], f[i] = 0; f[0] = 1; for(int i = 1; i <= n; i++) { if(i + a[i] <= n) f[i + a[i]] |= f[i - 1]; if(i - a[i] - 1 >= 0) f[i] |= f[i - a[i] - 1]; } cout << f[n] << endl; } return 0; }
想到是选一段异或出来是某个数平方了, 但是脑抽了忘记异或前缀和这个性质了
有了前缀和这个性质,那么我们只需要对每个位置找出他之前有出现能与他异或成某个数平方数的个数即可
再容斥一下就是答案
#include <bits/stdc++.h> using namespace std; #define endl "\n" typedef long long ll; const int N = 2e5 + 10; int a[N], pre[N], num[N * 4]; int main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int T = 1; cin >> T; while(T--) { memset(num, 0, sizeof(num)); int n; cin >> n; num[0]++; for(int i = 1; i <= n; i++) cin >> a[i], pre[i] = pre[i - 1] ^ a[i]; ll ans = 0; for(int i = 1; i <= n; i++) { int cnt = i; for(int j = 0; j <= 650; j++) { int now = j * j, nd = now ^ pre[i]; if(num[nd]) cnt -= num[nd]; } num[pre[i]]++; ans += cnt; } cout << ans << endl; } return 0; }
水题, 二分最大值判断是否有交集即可
#include <bits/stdc++.h> using namespace std; #define endl "\n" typedef long long ll; const int N = 1e5 + 10; int x[N], t[N], n, ans; bool check(int mid) { int l = 0, r = 1e9; for(int i = 1; i <= n; i++) { int now = mid - t[i]; l = max(l, x[i] - now); r = min(r, x[i] + now); } if(l <= r) {ans = l; return true;} return false; } int main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int T = 1; cin >> T; while(T--) { cin >> n; for(int i = 1; i <= n; i++) cin >> x[i], x[i] *= 2; for(int i = 1; i <= n; i++) cin >> t[i], t[i] *= 2; int L = 0, R = 1e9; while(L + 1 < R) { // cout << L << ' ' << R << endl; int M = (L + R) >> 1; if(check(M)) R = M; else L = M; } cout << fixed << setprecision(6) << ans / 2.0 << endl; } return 0; }
一个东西有两种属性,我们转化到二维平面即可,当时第一反应是扫描线去扫就行,但是数据范围很小,我们可以采取更简单的二维前缀和
#include <bits/stdc++.h> using namespace std; #define endl "\n" typedef long long ll; const int N = 1e3 + 10; ll a[N][N], pre[N][N]; int main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int T = 1; cin >> T; while(T--) { memset(a, 0, sizeof(a)); memset(pre, 0, sizeof(pre)); int n, q; cin >> n >> q; for(int i = 1; i <= n; i++) { int x, y; cin >> x >> y; a[x][y] += x * y; } for(int i = 1; i <= N - 10; i++) { for(int j = 1; j <= N - 10; j++) { pre[i][j] = pre[i - 1][j] + pre[i][j - 1] + a[i][j] - pre[i - 1][j - 1]; } } while(q--) { int x1, x2, y1, y2; cin >> x1 >> y1 >> x2 >> y2; x1++; x2--; y1++; y2--; cout << pre[x2][y2] - pre[x1 - 1][y2] - pre[x2][y1 - 1] + pre[x1 - 1][y1 - 1] << endl; } } return 0; }
假设区间$[1,5]$满足条件
那么$a_1+a_3+a_5=a_2+a_4$
转换一下公式$(a1-a2)+(a_3-a_4)+a_5=0$
奇数系数为1,偶数系数为-1,求一个前缀和,那么假如当前的差值前缀和在之前出现过则存在一段满足$(a1-a2)+(a_3-a_4)+a_5=0$
#include<bits/stdc++.h> using namespace std; #define endl "\n" #define int long long typedef long long ll; // 猜出题方向,考什么知识点 const int N = 2e5 + 10; int a[N], pre[N]; void solve() { int n; cin >> n; for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 2; i <= n; i += 2) a[i] = -a[i]; set<int> s; s.insert(0); int sum = 0; for (int i = 1; i <= n; i++) { sum += a[i]; if(s.count(sum)) { cout << "YES" << endl; return; } s.insert(sum); } cout << "NO" << endl; } signed main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int T = 1; cin >> T; while(T--) solve(); // solve(); return 0; }
浙公网安备 33010602011771号