余利区

 找回密码
 立即注册
查看: 123|回复: 20

2022 ACM-ICPC 网络赛(1) 个人题解 更新至5题

[复制链接]

2

主题

4

帖子

10

积分

新手上路

Rank: 1

积分
10
发表于 2022-9-21 09:38:30 | 显示全部楼层 |阅读模式
如何补题 PTA | 程序设计类实验辅助教学平台 ,然后不要选择买时光机。


如果没有金币可以在上面的签到领取


C
题意:
给定一棵树,你可以执行以下操作。
(1)删除一个点
(2)删除一个度数为2的点,但是会把这个点连接的两个点用一条新边来连接


问把树的点都删除,最少需要多少个操作(1)
做法:
因为每个操作都只会删除一个点,所以我们有 (1) + (2)  = n
还发现,当执行一个操作(2)后,会相连的两个点的度数不会发生改变。


而度数为1的点,只能由操作(1)删除,并且会是的相连那个点的度数减一
并且我们手玩样例发现,叶子节点的个数就是答案


H
题意:
有一个自己写的语言,由固定的语法。问library执行了多少次。
实际上,我们可以理解为对下面的字符串进行求值
(((1 +  1 + 1) * 3 )* 3 + 1 )*12
做法:
我们直接用栈来模拟既可,如果你不会的话,可以在力扣上搜索”计算器“


D
题意:
定义函数 ctz(x) 为x的二进制下后缀0的个数例如 ctz(1001000_2) = 3
pop(x) 为 x 的二进制下1的个数 pop(100101_2) = 3
定义一个数 x 是好数,需要满足 ctz(x) = pop(x) 。
现在给出 10^5 次询问,每次给出一个区间 [l,r](l,r \le 10^9) ,问这个区间中是否有一个好数。
做法:
比赛的时候打表发现,1e9范围内的好数只有5e5个,所以我们直接dfs暴力处理出所有的好数,最后排序二分搜索即可。
严格鸽:ICPC 网络赛 D 的几种枚举方式
A
题意:
给出一个01字符串组成的环
你可以执行删除操作,选择一个1,删除这个数,及其左右两边的数。
例如


如果一个字符串可以被完全删除,则称这个字符串是个好字符串
现在给出 10^6  次询问,每次询问给出一个区间 [l,r] 保证区间长度是3的倍数。
问 s[l...r] 是不是一个好字符串。
做法:
我们发现,如果长度为3的话,那么只要有一个1就可以完全删除。
如果长度为6的话。理论上两个1就可以完全删除
但是,以下情况不行


也就是两个1相邻。
但是只要两个1不相邻的话,我们就可以删除然后变成长度为3只有一个1的情况


那么我们看下长度为9的情况


所以发现,只要字符串中,不相邻的1的个数大于等于区间长度 / 3的话,就可以完全删除了。
但是如果需要连续的1的呢?我们可以假装他是多个不相邻的1


那么对于一段区间,我们拆成几个部分。


即左右端点伸展的连续的1的个数,和中间的一段。
那么这个是可以预处理出来
(这里我们放上赛时的代码,为什么会保存赛时的代码呢?因为队友阿汤在调试的时候点错了,直接点提交了,然后在一阵卡顿后,AC了.....
code
void slove() {
    cin >> n >> m;
    string s; cin >> s;
    s = "?" + s;
    int cnt = 0;
    for (int i = 1; i <= n; i++) {
        if (s == '1') cnt++;
        else cnt = 0;
        f = f[i - cnt - 1] + cnt / 2 + cnt % 2;
        pre = cnt;
    }
    cnt = 0;
    for (int i = n; i; i--) {
        if (s == '1') cnt++;
        else cnt = 0;
        last = cnt;
    }
    while (m--) {
        int tmp = 0;
        int l, r; cin >> l >> r;
        tmp = pre[r] + last[l];
        int st = f[r - pre[r]] - f[l + last[l]] + tmp / 2 + tmp % 2;
        if ((r - l + 1) / 3 <= st) cout << 0 << endl;
        else cout << (r - l + 1) / 3 - st << endl;
    }
}
L
题意:
给定两个字符串 s,t(|s|,|t| \le 5*10^5)
你需要在 s 中找到一个最长的子序列 s' ,满足 s' 和 t 的任意一个公共子序列,长度都不超过1。
输出 s' 的长度。
做法:
我们预先处理出一个ban的可能,如果 ban[a] = 1 ,则表明在选择的 s' 中,不能出现 a....b 这样的
for (int i = m; i; i--) {
    for (int j = 0; j < 26; j++)
        if (vis[j]) ban[t][j] = 1;
    vis[t] = 1;
}
然后我们定义, dp[j] 为选择 [1...i] 的子串构成的子序列,最后一个字符为 j 的最大长度。
考虑转移,如果 s 都没有在 t 中出现过,我们直接 dp[j] = dp[i - 1][j] + 1 。
你可能会奇怪,不是应该以 s 为结尾吗?但是因为 s 都没有在 t 中出现过,例如
s = aaeebb,t = ab ,这个e我们是可以随便选的。
(其实dp[j]的准确定义为[1...i]中最后一个为j(j在t中出现过)的最大长度)
然后我们枚举 s 放到某个 j 后面,我们需要保证 (j,s) 是没有被ban过的
for (int j = 0; j < 26; j++) {
    if (!ban[j][s])
        dp[s] = max(dp[s], dp[i - 1][j] + 1);
}
然后你可能又会奇怪,你只是保证了相邻的两个字符没有没ban,不会出现这种
(a,b)(b,c) 没有被ban (ban[a] = 0,ban[c] = 0
但是 (a,c) 被ban了( ban[a][c] = 1 )


我们考虑下是否会有这种情况。
因为 (a,c) 被ban了,所以在t中 a 一定在 c 前面


我们考虑 b 的位置。
(b,c)被ban了


(a,b)(b,c)被ban了


(a,b)被ban了


也就是说,不会有这种情况,所以直接转移吧!
code
void slove() {
    cin >> s + 1;
    cin >> t + 1;
    int n = strlen(s + 1), m = strlen(t + 1);
    for (int i = 1; i <= n; i++)s -= 'a';
    for (int i = 1; i <= m; i++)t -= 'a';
    for (int i = m; i; i--) {
        for (int j = 0; j < 26; j++)
            if (vis[j]) ban[t][j] = 1;
        vis[t] = 1;
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < 26; j++) {
            dp[j] = dp[i - 1][j] + (vis[s] == 0);
        }
        if (vis[s] == 0)continue; //没有在t中出现的字符已经在上面转移完了
        dp[s] = max(dp[s], 1);//初始化
        for (int j = 0; j < 26; j++) {
            if (!ban[j][s])
                dp[s] = max(dp[s], dp[i - 1][j] + 1);
        }
    }
    int ans = 0;
    for (int i = 0; i < 26; i++)
        ans = max(ans, dp[n]);
    cout << ans << "\n";
}
回复

使用道具 举报

3

主题

7

帖子

12

积分

新手上路

Rank: 1

积分
12
发表于 2022-9-21 09:38:45 | 显示全部楼层
D是有啥剪枝操作吗昨天写T了
回复

使用道具 举报

2

主题

4

帖子

7

积分

新手上路

Rank: 1

积分
7
发表于 2022-9-21 09:38:57 | 显示全部楼层
队友写的,说是和全排列一样就可以。看别人也有带着放了多少个1一起跑,剩下的位置都放0,如果0的个数加1的个数大于30就return
回复

使用道具 举报

2

主题

6

帖子

10

积分

新手上路

Rank: 1

积分
10
发表于 2022-9-21 09:39:56 | 显示全部楼层
就5e5个数字,正常枚举咋会t
回复

使用道具 举报

2

主题

6

帖子

9

积分

新手上路

Rank: 1

积分
9
发表于 2022-9-21 09:40:29 | 显示全部楼层
我是从低位到高位枚举01的
回复

使用道具 举报

3

主题

5

帖子

10

积分

新手上路

Rank: 1

积分
10
发表于 2022-9-21 09:41:09 | 显示全部楼层
那,感觉,会不会很容易重复啊
回复

使用道具 举报

1

主题

4

帖子

7

积分

新手上路

Rank: 1

积分
7
发表于 2022-9-21 09:41:17 | 显示全部楼层
不知道啊应该是我枚举的有问题吧
回复

使用道具 举报

0

主题

3

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2022-9-21 09:42:13 | 显示全部楼层
当时输出了size好像有1e6
回复

使用道具 举报

1

主题

7

帖子

10

积分

新手上路

Rank: 1

积分
10
发表于 2022-9-21 09:42:18 | 显示全部楼层
不能直接爆搜30位,需要枚举二进制后面0的个数,设为k, 第k+1位设为1,然后去dfs前30-k-1位的情况,记得设置一个1的lim上限为 k-1,这是个组合数复杂度[潜水]
回复

使用道具 举报

2

主题

5

帖子

8

积分

新手上路

Rank: 1

积分
8
发表于 2022-9-21 09:42:59 | 显示全部楼层
谢谢佬
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

云顶设计嘉兴有限公司模板设计.

免责声明:本站上数据均为演示站数据,如购买模板可以上DISCUZ应用中心购买,欢迎惠顾.

云顶官方站点:云顶设计 模板原创设计:云顶模板   Powered by Discuz! X3.4© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表