PRE 杯(#2026.1.30 Lv.1)题解
A
题面链接
相关算法 tag :分治
题意理解
意思很明确,题目也保证了括号匹配的合法性,直接分治即可。
正文
思路
不过多阐述,十分好想到分治。
实现方法
\(O(L)\) 计算括号串匹配,每个括号就是一个同样的问题,权值相乘即可
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6 + 5;
int t,n,m,pos[N];
string st;
map <string, long long> q;
void start (int l, int r, ll k){
for (int i = l; i <= r;){
if (st[i] == '('){
int now = pos[i];
if (now + 1 <= r && st[now + 1] == '_'){
ll xsum = 0;
now += 2;
while (st[now] >= '0' && st[now] <= '9' && now <= r)
xsum = xsum * 10 + st[now] - '0', now++;
start (i + 1, pos[i] - 1, k * xsum);
}
else start (i + 1, pos[i] - 1, k);
i = now;
}
else{
int now = i;
string s = ""; s += st[now];
while (st[now + 1] >= 'a' && st[now + 1] <= 'z' && now + 1 <= r)
s += st[now + 1], now++;
if (now == r || st[now + 1] != '_'){
q[s] += k;
now ++;
}
else{
now += 2;
ll xsum = 0;
while (st[now] >= '0' && st[now] <= '9' && now <= r)
xsum = xsum * 10 + st[now] - '0', now++;
q[s] += k * xsum;
}
i = now;
}
}
}
int main (){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> t;
while (t--){
q.clear();
cin >> n >> m;
for (int i = 1; i <= n; i++){
cin >> st;
stack <int> pre;
for (int j = 0; j < st.size(); j++){
if (st[j] == '('){
pre.push(j);
}
else if (st[j] == ')'){
pos[pre.top()] = j;
pre.pop();
}
}
start (0, st.size() - 1, 1);
}
for (int i = 1; i <= m; i++){
cin >> st;
cout << q[st] << '\n';
}
}
}
B
题面链接
相关算法 tag :字符串,枚举
题意理解
意思很明确,需要注意的是只有都存在才能加进集合,不是看所有的最大值的最小值,也要注意集合内的元素不一定连续。
正文
思路
\(O(n ^ 2)\) 预处理回文串,暴力的话是 \(O(n ^ 4)\) 的,加点优化预期能过 \(10\) 多个点。
\(O(n ^ 4)\) 代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 5005;
char s[N];
int n,ans,f[N][N],b[N][N];
int main()
{
ios::sync_with_stdio(0);
cin.tie(nullptr); cout.tie(nullptr);
cin >> n >> s + 1;
for (int i = 1; i <= n; i++) if (s[i] >= 'a') s[i] = s[i] - 'a' + 'A';
for (int i = 1; i <= n; i++) f[i][i] = 1;
for (int i = 1; i < n; i++) if (s[i] == s[i + 1]) f[i][i + 1] = 2;
for (int len = 3; len <= n; len++){
for (int i = 1; i <= n; i++){
int j = i + len - 1;
if (j > n) break;
if (s[i] == s[j] && f[i + 1][j - 1] > 0) f[i][j] = len;
}
}
for (int len = 1; len <= n; len++){
for (int k = len; k >= 1; k--){
bool now = 1;
for (int j = 1; j + len - 1 <= n; j++){
bool flag = 0;
int l = j, r= j + len - 1;
for (int p = l; p + k - 1 <= r; p++){
if (f[p][p + k - 1] > 0){
flag = 1;
break;
}
}
if (!flag){
now = 0;
break;
}
}
if (now){
ans += k;
break;
}
}
}
cout << ans << '\n';
}
但这肯定过不了这题,需要优化查询到 \(O(n ^ 2)\)。考虑枚举所有子串的时候多余时间开支很大,方向就为实现 \(O(1)\) 算出任意子串的存在的回文子串的长度。
一个有意思的思路
手搓样例 AbccbA,将对应的回文子串标在坐标轴上,横轴为起点,纵轴为终点,如下:

显然处于直线 \(y = x + k\) 上的点的权值为 \(k + 1\)。下标 \(l\) 到 \(r\) 的子串的回文子串对应的点处于直线 \(x = l\) ,直线 \(y = r\) 和直线 \(y = x\) 围成的三角区域内或边上,上图为 \(l = 2\) 及 \(r = 5\) 的示例。
那问题就清晰了,如果直线 \(y = x + k\) 上没有点,那么 $\forall i \in [1,n] $ 且 \(i \in \mathbb {N}\),\(k + 1 \notin f(i)\);如果有,那么只有当 \(i\) 大于等于直线 \(y = x + k\) 上任意两点之间横坐标差值的最大值(外加第一个点与 \(0\) 比,最后一个与 \(n + 1\) 比)加上 \(k - 1\) 时,\(k + 1 \in f(i)\)。
实现
想必很简单了,与上面的只有查询不同。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 5005;
char s[N];
int n,f[N][N],ans,b[N];
int main()
{
ios::sync_with_stdio(0);
cin.tie(nullptr); cout.tie(nullptr);
cin >> n >> s + 1;
for (int i = 1; i <= n; i++) if (s[i] >= 'a') s[i] = s[i] - 'a' + 'A';
for (int i = 1; i <= n; i++) f[i][i] = 1;
for (int i = 1; i < n; i++) if (s[i] == s[i + 1]) f[i][i + 1] = 2;
for (int len = 3; len <= n; len++){
for (int i = 1; i <= n; i++){
int j = i + len - 1;
if (j > n) break;
if (s[i] == s[j] && f[i + 1][j - 1] > 0) f[i][j] = len;
}
}
for (int i = 1; i <= n; i++){
bool flag = 0;
int l = 1,r = i,las = 0, le = 0;
while (r <= n){
if (f[l][r] > 0){
le = max (le, l - las - 1);
las = l;
flag = 1;
}
l++;r++;
}
if (flag){
le = max (le, l - las - 1);
le += i;
b[le] = max (b[le], i);
}
}
for (int i = 1; i <= n; i++){
b[i] = max (b[i - 1], b[i]);
ans += b[i];
}
cout << ans << '\n';
}
C
题面链接
相关算法 tag :栈,模拟,贪心
题意理解
平衡力的定义就是合法括号串的定义,然后就没了 qaq
正文
下面都以合法括号串代替题中的平衡力
开始思路
首先这类问题都先想想简单的情况怎么做,因为这明显是区间匹配,考虑从单个符号(即 test \(14\))的情况入手。
那只会增,删一个的情况又如何解决呢?其实这很经典了,我们令 \(dp_i\) 表示以 \(i\) 结尾的合法括号串个数,\(match\) 表示当前位置匹配的左括号的位置,那么
如果没有匹配左括号,自然 \(dp_i = 0\)。知道了这些自然就随便写代码了,用栈存储未匹配的左括号,输入右括号就匹配,删除时减去当前末尾的 \(dp\) 值,并删除对应的括号。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 5000005;
ll n,opt,x,now,ans,f[N],b[N];
stack <ll> q;
int main()
{
ios::sync_with_stdio(0);
cin.tie(nullptr); cout.tie(nullptr);
char c;
cin >> n;
for (int i = 1; i <= n; i++){
cin >> c >> x >> opt;
if (c == 'L')
for (int j = 1; j <= x; j++){
now++;
q.push (now);
f[now] = 0;
b[now] = -1;
}
else if (c == 'R')
for (int j = 1; j <= x; j++){
now++;
if (q.size()){
f[now] = f[q.top() - 1] + 1;
ans += f[now];
b[now] = q.top();
q.pop();
}
else{
f[now] = 0;
b[now] = -2;
}
}
else if (c == 'D')
for (int j = 1; j <= x; j++){
ans -= f[now];
if (b[now] > 0) q.push (b[now]);
else if (b[now] == -1) q.pop();
f[now] = 0;
now--;
}
if (opt == 1) cout << ans << '\n';
}
}
拿了足足 \(50\) 分!
满分思路
有了上面的铺垫距离想出正解只有一步之遥了,我们仔细分析题目的数据范围,\(x_3 \le 10\) 明显是让我们能够暴力修改,\(x_1,x_2 \le 10 ^ 9\) 明显是让我们能够区间匹配,我们就按照题目来。
分析之前的添加左括号入栈及右括号匹配
我们单个的操作太耗费时间和空间,注意到对于任意的左括号明显它的 \(dp\) 值一定是 \(0\),我么自然可以将左括号压缩成一个块来处理,记录一下它的下标起点 \(l\) 及终点 \(r\),就能表示整个左括号块,为了方便右括号匹配,我们也可以记录该块的剩余括号个数 \(res\)。对于右括号的匹配,因为我们知道对于任意的左括号它的 \(dp = 0\),那么如果当前的右括号匹配的左括号前面还是左括号,显然它的 \(dp = 1\)。一个显而易见的方法出来了:我们不需要直接存储括号序列,仅需通过存储对应的类型和下标表示,匹配时我们也不需要一个一个的匹配,可以直接匹配一个左括号块,只有将一个块的左括号全部匹配完时,\(dp\) 值才可能大于 \(1\),我们将两种情况分开即可。对于 \(dp\) 值的存储,也只需存储一个右括号块的 \(r\) 下标的 \(dp\) 值,使用哈希即可。
分析之前的删除
显然删除是不需要改逻辑的,但是我们变更了插入及匹配,删除就要随之改变,但也无非就是将修改单个括号改成了修改一个块。
实现框架
声明的量
一个左括号栈,存储下标和剩余值;一个总栈,存储下标和类型,左括号要额外存储剩余值,右括号要额外存匹配下标;一个哈希表,存储 \(dp\) 值。
L 操作
将块插入左括号栈和总栈。
R 操作
将块插入总栈,更该对应的左括号栈中的块,若失配则额外处理。
D 操作
对总栈中的块修改,更该对应的下标,或直接删块。或恢复对应的左括号块,或不需要。更改答案。
代码
建议自己先写一下再看。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 4000005;
char c,las;
ll ans,len;
int n,x,opt;
struct node1 {
ll l,r;
ll ml,mr;
ll k;
char ty;
};
struct node2 {ll l,r,res;};
node1 s[N];
node2 q[N];
int ss,qs;
unordered_map<ll,ll> pos;
int main()
{
ios::sync_with_stdio(0);
cin.tie(nullptr); cout.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++){
cin >> c >> x >> opt;
if (x == 0){
if (opt == 1) cout << ans << '\n';
continue;
}
if (c == 'L'){
q[++qs].l = len + 1;
q[qs].r = len + x;
q[qs].res = x;
s[++ss].l = len + 1;
len += x;
s[ss].r = len;
s[ss].k = -1; s[ss].ty = 'L';
}
else if (c == 'R'){
if (s[ss].k == -2 && s[ss].ty == 'R'){
s[++ss].l = len + 1;
len += x;
s[ss].r = len;
s[ss].k = -2; s[ss].ty = 'R';
pos[s[ss].r] = 0;
}
else{
ll res = x;
while (qs > 0 && res > 0){
if (q[qs].res > res){
s[++ss].ml = q[qs].r - res + 1;
s[ss].mr = s[ss].ml + res - 1;
q[qs].res -= res;
q[qs].r -= res;
s[ss].l = len + 1;
len += res;
s[ss].r = len;
s[ss].k = -3; s[ss].ty = 'R';
pos[s[ss].r] = 1;
ans += s[ss].r - s[ss].l + 1;
res = 0;
}
else{
ll ns = q[qs].res;
if (ns != 1){
s[++ss].ml = q[qs].l + 1;
s[ss].mr = q[qs].r;
s[ss].l = len + 1;
s[ss].r = len + ns - 1;
s[ss].k = -3; s[ss].ty = 'R';
pos[s[ss].r] = 1;
ans += s[ss].r - s[ss].l + 1;
}
len += ns;
s[++ss].l = len; s[ss].r = len;
s[ss].ml = s[ss].mr = q[qs].l;
s[ss].k = q[qs].l - 1; s[ss].ty = 'R';
pos[s[ss].r] = pos[s[ss].k] + 1;
ans += pos[s[ss].r];
qs--;
res -= ns;
}
}
if (qs == 0 && res > 0){
s[++ss].l = len + 1;
len += res;
s[ss].r = len;
s[ss].k = -2; s[ss].ty = 'R';
pos[s[ss].r] = 0;
}
}
}
else{
len -= x;
ll res = x;
while (res > 0){
if (s[ss].k == -1){
ll rsl_2 = q[qs].res;
if (rsl_2 > res){
q[qs].res -= res;
q[qs].r -= res;
s[ss].r -= res;
}
else{
s[ss].r -= rsl_2;
qs--;
}
if (s[ss].r < s[ss].l) ss--;
res -= min (res, rsl_2);
continue;
}
ll rsl = s[ss].r - s[ss].l + 1;
if (s[ss].k == -2){
if (rsl > res) s[ss].r -= res;
else ss--;
res -= min (res, rsl);
continue;
}
if (s[ss].k < 0){
if (rsl > res){
pos[s[ss].r] = 0;
q[++qs].l = s[ss].ml;
q[qs].r = s[ss].ml + res - 1;
q[qs].res = res;
s[ss].ml += res;
s[ss].r -= res;
pos[s[ss].r] = 1;
}
else{
q[++qs].l = s[ss].ml;
q[qs].r = s[ss].mr;
q[qs].res = q[qs].r - q[qs].l + 1;
pos[s[ss].r] = 0;
ss--;
}
ans -= min (res, rsl);
}
else{
q[++qs].l = s[ss].ml;
q[qs].r = s[ss].mr;
q[qs].res = 1;
ans -= pos[s[ss].r];
pos[s[ss].r] = 0;
ss--;
}
res -= min (res, rsl);
}
}
if (opt == 1){
cout << ans << '\n';
}
}
}
D
题面链接
暂时没有写 qaq
E
题面链接
暂时没有写 qaq

浙公网安备 33010602011771号