1.12 CW 模拟赛 赛时记录

看题

不是哥们怎么感觉一堆原题但是都不会做
没复习最悲惨的一次

策略肯定还是暴力, 没有什么看上去简单的题

T1

思路#

侥幸心理找了一下没有啊, 必须自己想

合法串显然就是满足匹配的串
考虑这种经典问题的常见转化 : 令 (1 , )1 , 合法括号串仅当其任意前缀 0 且全串之和 =0

先考虑确定 i 时怎么计算
首先计算 T[i:n] 中的前缀, 记录所有串和 0 的, >0 的必定没有作用
然后考虑 T[1:i] 中的后缀, 记录所有串和 0 的, <0 的必定没有作用
有点感性, 但是应该是这样的

考虑答案合并
fi 表示对于 T[i:n] 中的前缀, 所有串和 =i 串串的个数, 令 gi 表示对于 T[1:i1] 中的后缀, 所有串和 =i 的串串的个数
容易发现 gi,fi 可以组成一个合法串
考虑方案数即为 fi×gi

感觉就是这样, 手摸下样例

发现一些问题, fi 还要新开一维表示串和, 但是显然是做不到的
fi,j 表示对于所有以 i 开头的串串, 所有串和 =j 串串的个数, 令 gi,j 表示对于所有以 i 结尾的串串, 所有串和 =j 的串串的个数, 考虑怎么做到对于每一个 i , 快速算出 f,g

好消息是 O(n2) 能做了

考虑的更深一点, 其实可以对于全串的前后缀做处理进而找到 f,g
问题变成怎么做才能规避掉对前后缀串和的枚举


理一下

首先是一个基础:
对于 i 位置结果的统计, 你只需要考虑

  • i 结尾的后缀中, 串和 0
  • i 开头的前缀中, 串和 0

误区在于你还是要统计不满足考虑条件的作为转移, 不然会漏掉情况

那么具体的, 我们设计状态
fi,j 表示以 i 结尾的后缀中, 串和为 j
gi,j 表示以 i 开头的前缀中, 串和为 j

f,g 的状态转移是容易做到 O(n) 的, 考虑 i 点的答案
容易做到分类讨论

  • 前缀 + i + 后缀
  • 前缀 + i
  • i + 后缀

非常经典的分类, 以后一定要注意不要漏了


但是你发现瓶颈在枚举 j 上, 怎么办, 必须解决这个问题

这个可能要改一下状态, 或者整体处理
不太会了

实现 30pts#

框架#

如上讨论即可

代码#

#include <bits/stdc++.h>
#define int long long
const int MAXN = 5206; //41
const int MOD = 1e9 + 7;
namespace calc {
    int add(int a, int b) { return a + b > MOD ? a + b - MOD : a + b; }
    int sub(int a, int b) { return a - b < 0 ? a - b + MOD : a - b; }
    int mul(int a, int b) { return (a * b * 1ll) % MOD; }
    void addon(int &a, int b) { a = add(a, b); }
} using namespace calc;

int n;
std::string bra;
int a[MAXN];
std::map<int, int> f[MAXN], g[MAXN];

signed main()
{
    std::cin >> bra; n = bra.length(); bra = ' ' + bra;
    for (int i = 1; i <= n; i++) a[i] = (bra[i] == '(') ? 1 : -1, f[i][a[i]] = 1, g[i][a[i]] = 1;

    /*转移*/
    for (int i = 1; i <= n; i++) for (int j = -n; j <= n; j++) addon(f[i][j], f[i - 1][j - a[i]]);
    for (int i = n; i >= 1; i--) for (int j = -n; j <= n; j++) addon(g[i][j], g[i + 1][j - a[i]]);

    int ans = 0;
    /*统计答案*/
    for (int i = 1; i <= n; i++) {
        int res = 0;
        for (int j = ((a[i] == 1) ? -1 : 1); j <= n; j++) addon(res, mul(f[i - 1][j], g[i + 1][-j - a[i]]));
        if (a[i] == 1) addon(res, g[i + 1][-a[i]]);
        if (a[i] == -1) addon(res, f[i - 1][-a[i]]);
        addon(ans, mul(res, i));
    }
    printf("%lld", ans);

    return 0;
}

T2

T1 想乱了, 先去想 T2 , 回头再看 T1
甚至 T2 也像做过的题

很危险, 赶紧把该拿的分拿了, 不要在死磕
先不管 T1

今天的策略是把后面的题暴力打了死磕 T1

思路#

这个题绝对是做过的

原来至少三位数是这样的吗

好吧那不管这道题

T3

目前得分: 130 pts
虽然那 100 应该不能算

思路#

首先把每一行删除一位之后结果相同的删除选择并到一起
具体的, 如果删除 ai,aj 对于 i 行的结果一样, 那么把 i,j 加入同一个集合, 假设第 i 行对应的操作集合为 Si,k

考虑 dp , 令 fi,j 表示考虑到第 i 行, 其中这一行删除的位置属于 j 集合的方案数
考虑转移

fi,jSi1,kSi,jfi1,k

其中并集特殊实现
复杂度 O(n3) , 完全足够了, 开打

总结

T1 思路不够优秀, 没有意识到这一点浪费了太多时间
T3,T4 部分分很多, 没有意识到这一点

以后一定要对自己的水平有清楚认知, 做好策略, 然后按照时间分配打下去, 切莫死磕

posted @   Yorg  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示