2024.09.17模拟赛总结

破防了破防了破防了破防了破防了破防了破防了破防了破防了破防了破防了破防了破防了破防了破防了破防了破防了破防了破防了破防了破防了破防了破防了破防了破防了
难度整体波动,局部递增。T2<T4<T1<T3,我竟然对着 T1 想这么久。


T1

怎么每次 rfy 模拟赛,T1 都这么难。
想了大半场比赛,结果还没做出来,要是换成 T2 应该能过。

似乎被样例 hack 了。
首先考虑 spj 判断答案是否正确的过程,大概是贪心的往后找,直接判,如下:

int x=0,y=0,ba=0; for(auto c:w) { while(x<a.size()&&a[x]!=c) ++x; if(x==a.size()) ba|=1; else ++x; while(y<b.size()&&b[y]!=c) ++y; if(y==b.size()) ba|=2; else ++y; } if(ba!=3) s.quitf(_wa,"subseq req not filled");

于是不难想到 80 分的做法,设 fi,j 表示第一个串的 x 跳到了位置 i,第二个串的 y 跳到位置 j 时,最长不坏串长度。
但是这样做是 Θ(26n2) 的,所以会 TLE。
因为答案串长度是比较小的,大概是 n/13 级别的,于是考虑交换第二维和答案,贪心的想,设 fi,j 表示第一个串的 x 跳到了位置 i,长度为 j 时,y 跳到最靠右的位置是多少。
转移时,记录对于每个位置,它后面每个字符出现的第一个位置,边界设 nxtn+1,i,0=n+1,最后符合即为 fn+1,pos=m+1pos 是最小长度,然后在转移同时记录转移过来的位置,不断往前跳,记录答案即可。

#include <bits/stdc++.h> using namespace std; const int N = 5010, inf = -0x3f3f3f3f; int n, m; string a, b; struct sb { int j, pre, lst_ch; } f[N][N]; int nxt[N][26][2]; sb Max(sb a, sb b) { if (a.j < b.j) return b; else return a; } int main() { freopen("string.in", "r", stdin); freopen("string.out", "w", stdout); int T; cin >> T; while (T -- ) { cin >> a >> b; n = a.size(), m = b.size(); a = " " + a, b = " " + b; for (int i = 0; i < 26; i ++ ) nxt[n + 1][i][0] = n + 1, nxt[n + 2][i][0] = n + 1, nxt[m + 1][i][1] = m + 1, nxt[m + 2][i][1] = m + 1; for (int i = n; i; i -- ) { for (int j = 0; j < 26; j ++ ) nxt[i][j][0] = nxt[i + 1][j][0]; nxt[i][a[i] - 'a'][0] = i; } for (int i = m; i; i -- ) { for (int j = 0; j < 26; j ++ ) nxt[i][j][1] = nxt[i + 1][j][1]; nxt[i][b[i] - 'a'][1] = i; } int lx = max(n, m) / 13 + 1; for (int i = 0; i <= n + 1; i ++ ) for (int j = 0; j <= lx; j ++ ) f[i][j] = {inf, -1, 0}; for (int i = 0; i <= lx; i ++ ) f[0][i].j = 0; for (int i = 0; i <= n; i ++ ) for (int j = 0; j <= lx; j ++ ) { if (f[i][j].j == inf) continue; for (int k = 0; k < 26; k ++ ) f[nxt[i + 1][k][0]][j + 1] = Max(f[nxt[i + 1][k][0]][j + 1], {nxt[f[i][j].j + 1][k][1], i, k}); } int k = 0; for (int j = 0; j <= lx; j ++ ) if (f[n + 1][j].j == m + 1) { k = j; break; } // cout << k << '\n'; stack<char> stk; int tmp = n + 1; while (tmp) { stk.push(f[tmp][k].lst_ch + 'a'); tmp = f[tmp][k].pre; k -- ; } while (stk.size()) cout << stk.top(), stk.pop(); cout << '\n'; } return 0; }

T2

一直在想 T1,结果 T2 只打了个暴力,T2 相对 T1 还是容易的。
fi,x,y 表示前 i 个数,1 抽出 x 个,2 抽出 y 个的车数和最后一辆车的装载量(是 pair 类型,甚至没有看到 ai=1/2)。
然后转移先不考虑抽出的装载,只考虑在原序列中的装载,抽出的到最后再计算。
fi,x,y=min(fi1,x1,y[x>0],fi1,x,y1[y>0],f[i1][x][y]+a[i])+ai 可以重载运算符一下,x,y 的范围用前缀和预处理出前 i 个数中 1/2 的个数。
最后答案在 f[n][x][y] 统计即可。
注意到会 MLE,所以需要开个滚动数组优化一下空间。

#include <bits/stdc++.h> #define inf 0x3f3f3f3f #define need(x) (bool)x.flow + x.cnt_car #define sum(x) x.cnt_car * w + x.flow using namespace std; const int N = 410; int n, w; int a[N], sum1[N], sum2[N]; struct sb { int cnt_car, flow; } f[2][N][N]; sb operator + (sb a, int b) { return {a.flow + b >= w ? a.cnt_car + 1 : a.cnt_car, a.flow + b == w ? 0 : a.flow + b > w ? b : a.flow + b}; } sb mn(sb a, sb b) { return a.cnt_car >= inf ? b : b.cnt_car >= inf ? a : sum(a) > sum(b) ? b : a; } int main() { freopen("pack.in", "r", stdin); freopen("pack.out", "w", stdout); cin >> n >> w; for (int i = 1; i <= n; i ++ ) cin >> a[i]; for (int i = 1; i <= n; i ++ ) sum1[i] = sum1[i - 1] + (a[i] == 1), sum2[i] = sum2[i - 1] + (a[i] == 2); // for (int i = 1; i <= n; i ++ ) cout << sum1[i] << ' ' << sum2[i] << '\n'; memset(f, 0x3f, sizeof f); f[0][0][0] = {0, 0}; for (int i = 1; i <= n; i ++ ) { for (int x = 0; x <= sum1[i]; x ++ ) for (int y = 0; y <= sum2[i]; y ++ ) { f[i & 1][x][y] = {inf, inf}; if (x && a[i] == 1) f[i & 1][x][y] = mn(f[i & 1][x][y], f[(i - 1) & 1][x - 1][y]); if (y && a[i] == 2) f[i & 1][x][y] = mn(f[i & 1][x][y], f[(i - 1) & 1][x][y - 1]); f[i & 1][x][y] = mn(f[i & 1][x][y], f[(i - 1) & 1][x][y] + a[i]); // cout << (f[(i - 1) & 1][x][y] + a[i]).cnt_car << '\n'; } } // cout << inf * 10 << '\n'; // for (int x = 0; x <= sum1[n]; x ++ ) // for (int y = 0; y <= sum2[n]; y ++ ) // cout << f[n & 1][x][y].cnt_car*w+f[n&1][x][y].flow << "\n"[y != sum2[n]]; int res = inf, mi = inf; for (int x = 0; x <= sum1[n]; x ++ ) for (int y = 0; y <= sum2[n]; y ++ ) { auto tmp = f[n & 1][x][y]; // cout << tmp.cnt_car << ' ' << tmp.flow << '\n'; int tx = x, ty = y; while (tx || ty) { if (tx && (!ty || tmp.flow + 2 > w)) tx -- , tmp = tmp + 1; else ty -- , tmp = tmp + 2; } // cout << tmp.cnt_car << ' ' << tmp.flow << '\n'; // cout << need(tmp) << '\n'; if (mi > need(tmp)) { mi = need(tmp); res = x + y; } else if (mi == need(tmp)) res = min(res, x + y); } cout << res << '\n'; return 0; }

T3

考虑一个特殊性质:每段 L/R 只有 1 个。
不难想到直接从大到小放即可,然后在每段后面加入 L/R
L/R 抽象成 /+,那么每段就是不能跨越 x 轴,且要使得值尽量靠近 x 轴。
不难发现,直接在每段中按整个序列的开头,进行正负交错放是可行的。

#include <bits/stdc++.h> using namespace std; const int N = 100010; int n, a[N]; string s; char ch[2] = {'L', 'R'}; pair<int, char> ch1[N]; map<char, int> mp = {{'L', 0}, {'R', 1}}; vector<pair<int, char> > pos; bool cmp(int a, int b) { return a > b; } int main() { freopen("weight.in", "r", stdin); freopen("weight.out", "w", stdout); cin >> n; for (int i = 1; i <= n; i ++ ) cin >> a[i]; cin >> s; int num = 0; s = " " + s; for (int i = 1; i <= n; i ++ ) if (s[i] != s[i - 1]) { num ++ ; pos.push_back({i, s[i]}); } sort(a + 1, a + 1 + n, cmp); int num1 = num; for (int i = 0; i < pos.size(); i ++ ) ch1[pos[i].first] = {a[num1 -- ], pos[i].second}; pos.push_back({n + 1, 'a'}); int tmp = 0; cout << ch1[pos[0].first].first << ' ' << ch1[pos[0].first].second << '\n'; // cout << pos[0].second << ' '; for (int i = 1; i < (int)pos.size(); i ++ ) { int lst = pos[i - 1].first, nw = pos[i].first; for (int j = lst + 1; j < nw; j ++ ) tmp ^= 1, cout << a[ ++ num] << ' ' << ch[(mp[pos[0].second] + tmp) & 1] << '\n'; // cout << pos[i].second << '\n'; if (i != pos.size() - 1) cout << ch1[pos[i].first].first << ' ' << ch1[pos[i].first].second << '\n'; } return 0; }

T4

考虑根号分治,设置一个阈值 t=n,对于出现次数小于 t 的,直接暴力即可;对于大于的,使用树状数组。注意到一个条件:当一个颜色 col 满足其是区间 [j,i] 的众数时,一定有 (sum[i][col]sum[j1][col])×2>ij+1,用树状数组维护即可,因为会有负数,所以加上偏移量 n

#include <bits/stdc++.h> #define int long long #define lowbit(i) (i & -i) using namespace std; const int N = 400010; int n; int a[N], b[N]; map<int, int> mp; int tr[N]; bool st[N]; int cnt, num[N], sum[N]; void add(int x, int v) { for (int i = x; i <= n * 2; i += lowbit(i)) tr[i] += v; } int query(int x) { int res = 0; for (int i = x; i; i -= lowbit(i)) res += tr[i]; return res; } signed main() { freopen("sequence.in", "r", stdin); freopen("sequence.out", "w", stdout); cin >> n; for (int i = 1; i <= n; i ++ ) scanf("%lld", a + i); int t = sqrt(n) * 2, res = 0; for (int i = 1; i <= n; i ++ ) b[i] = a[i]; sort(b + 1, b + n + 1); for (int i = 1; i <= n; i ++ ) if (!mp.count(b[i])) mp[b[i]] = ++ cnt; for (int i = 1; i <= n; i ++ ) a[i] = mp[a[i]], num[a[i]] ++ ; for (int i = 1; i <= cnt; i ++ ) { if (num[i] <= t) continue; add(n + 1, 1); st[i] = 1; for (int j = 1; j <= n; j ++ ) sum[j] = 0; for (int j = 1; j <= n; j ++ ) { sum[j] = sum[j - 1] + (bool)(a[j] == i); res += query(sum[j] * 2 - j + n); add(sum[j] * 2 - j + n + 1, 1); } add(n + 1, -1); for (int j = 1; j <= n; j ++ ) add(sum[j] * 2 - j + n + 1, -1); } // cout << res << '\n'; for (int i = 1; i <= n; i ++ ) num[i] = 0; for (int i = 1; i <= n; i ++ ) { int mx = 0, col = 0; for (int j = i; j <= min(n, i + 2 * t - 1); j ++ ) { num[a[j]] ++ ; if (num[a[j]] > mx) { mx = num[a[j]]; col = a[j]; } if (j - i + 1 < 2 * mx && !st[col]) res ++ ; } for (int j = i; j <= i + 2 * t - 1; j ++ ) num[a[j]] = 0; } cout << res << '\n'; return 0; }

__EOF__

本文作者MafuyuQWQ
本文链接https://www.cnblogs.com/MafuyuQWQ/p/18417083.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   MafuyuQWQ  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 百万级群聊的设计实践
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
点击右上角即可分享
微信分享提示