2202.10.11 CSP-S 2021 测试总结
2022.10.11 CSP-S 2021 测试总结
这场打的好心累, \(T1\) 想了 \(1\) 个多小时才想出来的, \(T2\),\(T4\)题意赛时还没读明白。
\(T1\):廊桥分配
\(T1\) 感觉是这几道题里最水的一道题,暴力都能踩过,然而却想了 \(1\) 个多小时的优化。首先我们考虑它是按照先来后到的顺序,那么一定会按照来的时间进行排序,那么很容易想到 \(O(n^2)\) 的做法,我们可以用 \(sum_i\) 代表在分配给 \(i - 1\) 个廊桥后又分配了一个所多飞机数,那么我们每次只需要暴力的找到每个廊桥是否符合当前的飞机即可。但是数据给到了 \(1e5\),怎么优化呢?我们可以用一个 \(set\),将每个飞机到达的时间扔到 \(set\) 里,然后我们在用一个 \(map\) 存一下每个飞机的离开时间即可,这样我们就可以二分查找到符合的飞机了,并且很容易删除掉符合的飞机。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int M = 1e5 + 7;
set<int> s , ss;
unordered_map<int,int> a , b;
int sum1[M] , sum2[M];
signed main () {
freopen("airport.in","r",stdin);
freopen("airport.out","w",stdout);
ios::sync_with_stdio(0),cin.tie(0);
int n , m1 , m2;
cin >> n >> m1 >> m2;
int cnt = 0 ,sum =0 ;
for(int i = 1; i <= m1; ++ i) {
int l , r; cin >> l >> r;
a[l] = r;
s.insert(l);
}
s.insert(0x3f3f3f3f);
while(cnt < m1) {
sum ++;
int x;
for(auto i : s) {
x = i;
break;
}
while(x < 1e9) {
sum1[sum] ++;
cnt ++;
s.erase(x);
x = a[x];
x = *s.lower_bound(x);
}
}
for(int i = 1; i <= n; ++ i) sum1[i] += sum1[i - 1];
cnt = 0 , sum = 0;
for(int i = 1; i <= m2; ++ i) {
int l , r; cin >> l >> r;
b[l] = r;
ss.insert(l);
}
ss.insert(0x3f3f3f3f);
while(cnt < m2) {
sum ++;
int x;
for(auto i : ss) {
x = i;
break;
}
while(x < 1e9) {
sum2[sum] ++;
cnt ++;
ss.erase(x);
x = b[x];
x = *ss.lower_bound(x);
}
}
int ans = 0;
for(int i = 1; i <= n; ++ i) sum2[i] += sum2[i - 1];
for(int i = 0; i <= n; ++ i) ans = max(ans , sum1[i] + sum2[n - i]);//这里是从0开始!
cout << ans << '\n';
}
\(T2\):括号序列
\(T2\) 好难。赛时的时候打了个爆搜结果发现题读假了,然后果断放弃了。等作者写出来再来补吧。补出来了(看的题解)。这道题的难点在于读题和对于合法串的 \(check\),首先我们考虑什么样的串是合法的,如 \((*(*)*)\) 就不是一个合法的序列,\((*(*)(*)*)\)也不是,而 \((*(*)*(*))\) 则是一个合法的序列,为什么呢,因为我们可以将 \((*)*(*)\) 看做一个合法序列 \(A\),则变成了 \((*A)\),这样他就合法了,而前两种都无法通过转化变成这种。那怎么做呢。首先很容易想到爆搜,每次试填,复杂度大概是 \(O(3^n)\) 的,那怎么办采取优化呢,我们发现 \(n \le 500\),很像一个区间 \(dp\),那我们是否可以采取类似于区间 \(dp\) 的方法呢。答案是肯定的。我们首先要明确几种状态,下面用数字代替了
0:****...*****
编号为0的是全是 * 的情况
1:(.........)
编号为1是外面套一个括号的情况
2:(...)**..**(..)**..**
编号为2是以括号开头,以 * 结尾的情况
3:(..)**..**(..)**..**(..)
编号为3是以括号开头,以括号结尾的情况
4:**(..)**..**(..)
编号为4是以 * 开头以括号结尾的情况
5:**(..)**(..)**
编号为5是以 * 开头并结尾的情况(0是特殊的5的情况)
这样我们就可以很容易的进行 \(dp\) 设计了,我们令 \(f_{i,j,k}\) 为 \(l\) ~ \(r\) 区间内为编号 \(k\) 的方案数。我么可以得到转移方程:
\(f_{i,j,0}\)
直接特判即可
\(f_{i,j,1}\) \(=\) \(f_{i+1,l-1,0} + f_{i+1,l-1,2}+f_{i+1,l-1,3}+f_{i+1,l-1,4}\),
但是我们需要特判一下是否可以嵌套上括号
\(f_{i,j,2}\) \(=\) \(\sum_{l=i}^{j-1}f_{i,l,3} * f_{l + 1,0}\)
\(f_{i,j,3}\) \(=\) \(\sum_{l=i}^{j-1}(f_{i,l,3}+f_{i,l,2})*f_{l+1,j,1}+f_{i,j,1}\)
注意编号为 \(1\) 也是以括号开头和结尾的,也需要加上
\(f_{i,j,4}\) \(=\) \(\sum_{l=i}^{j-1}(f_{i,l,4}+f_{i,l,5})*f_{l+1,j,0}\)
\(f_{i,j,5}\) \(=\) \(\sum_{l=i}^{j-1}f_{i,l,4}*f_{l+1,j,0}\)
然后放一个简短的代码吧
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int M = 5e2 + 7 , mod = 1e9 +7;;
int f[M][M][6];
char s[M];
bool check(int l , int r) {return (s[l] == '(' || s[l] == '?') && (s[r] == ')' || s[r] == '?');}
signed main () {
int n , m; cin >> n >> m;
cin >> s + 1;
for(int i = 1; i <= n; ++ i) f[i][i - 1][0] = 1;
for(int i = 1; i <= n; ++ i) {
for(int j = 1; j <= n - i + 1; ++ j ){
int k = j + i - 1;
if(i <= m) f[j][k][0] = f[j][k - 1][0] && (s[k] == '*' || s[k] == '?');
if(i >= 2) {
if(check(j , k)) f[j][k][1] = (f[j + 1][k - 1][0] + f[j + 1][k - 1][2] + f[j + 1][k - 1][4] + f[j + 1][k - 1][3]) % mod;
for(int l = j; l <= k - 1; ++ l) {
f[j][k][2] = (f[j][k][2] + f[j][l][3] * f[l + 1][k][0] % mod) % mod;
f[j][k][3] = (f[j][k][3] + (f[j][l][2] + f[j][l][3]) * f[l + 1][k][1]% mod) % mod;
f[j][k][4] = (f[j][k][4] + (f[j][l][4] + f[j][l][5]) * f[l + 1][k][1]% mod) % mod;
f[j][k][5] = (f[j][k][5] + f[j][l][4] * f[l + 1][k][0] % mod) % mod;
}
}
f[j][k][5] = (f[j][k][5] + f[j][k][0]) % mod;
f[j][k][3] = (f[j][k][3] + f[j][k][1]) % mod;
}
}
cout << f[1][n][3];
}
\(T3\):回文
\(T3\) 比 \(T2\) 简单太多了。但是作者太弱了,赛时只想出来了一个爆搜。对于 \(a\) 数组,我们最先开始只能掐头或去尾,那么我们的第一个操作就知道了,下面我们只考虑第一步取开头的情况,我们设开头数字为 \(x\),我们考虑回文串的性质,那么最后一个操作是将另一个开头或结尾的数字扔到 \(b\) 数组里,所以我们考虑找到第二个 \(x'\),这样我们只需要在 \(x\) 与 \(x'\) 中间插入其他数就可以了。那么如何维护其他数呢?因为两个一样的数不能连续取,所以一定有一个在前一个在后,所以我们可以考虑用栈来处理,我们可以将在 \(x'\) 左边的数放到第一个栈 \(s_1\) ,右边的放到第二个栈 \(s_2\),那么我们会发现如果一个数是两个栈的栈顶,另一个在栈底,那么他们两个可以组成回文。我们只需每次判断栈顶是否符合,然后将栈底上移即可。但题目中又要求字典序最小,所以我们优先做 \(x'\) 左侧的,但是对于在不同的两个栈的数,我们会让取右侧的数尽量在后即可。
view code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int M = 1e6 + 7;
int a[M] , s1[M] , s2[M] , top1 , top2 , vis[M] , last1 , last2 , ans[M];
signed main () {
freopen("palin2.in","r",stdin);
freopen("out.out","w",stdout);
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t; cin >> t;
while(t -- ) {
int n; cin >> n;
top1 = 0 , top2 = 0;
last1 = 1 , last2 = 1;
for(int i = 1; i <= 2 * n; ++ i) cin >> a[i];
int pos;
for(int i = 2; i <= 2 * n; ++ i) {
if(a[i] == a[1]) {pos = i; break;}
}
for(int i = pos - 1; i > 1 ; -- i) s1[++ top1] = a[i];
for(int i = pos + 1; i <= 2 * n; ++ i) s2[++ top2] = a[i];
int cnt = 1;
while(cnt <= n) {
if(s2[last2] == s1[top1] && last2 <= top2 && top1 >= last1) {
last2 ++;
ans[++ cnt] = 1 , ans[2 * n - cnt + 1] = 2;
top1 --;
}
else if(s1[last1] == s1[top1] && top1 > last1) {
last1 ++;
ans[++ cnt] = 1 , ans[2 * n - cnt + 1] = 1;
top1 --;
}
else if(s2[last2] == s2[top2] && last2 < top2) {
last2 ++;
ans[++ cnt] = 2 , ans[2 * n - cnt + 1] = 2;
top2 --;
}
else if(s1[last1] == s2[top2] && top2 >= last2 && last1 <= top1) {
last1 ++;
ans[++ cnt] = 2 , ans[2 * n - cnt + 1] = 1;
top2 --;
}
else break;
}
if(cnt == n) {
ans[1] = 1;
ans[2 * n] = 1;
for(int i = 1; i <= 2 * n; ++ i)
if(ans[i] == 1) cout << 'L';
else if(ans[i] == 2) cout << 'R';
cout << '\n';
continue;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
top1 = 0 , top2 = 0;
last1 = 1 , last2 = 1;
for(int i = 1; i <= 2 * n; ++ i) {
if(a[i] == a[n * 2]) {pos = i; break;}
}
for(int i = pos - 1; i > 0 ; -- i) s1[++ top1] = a[i];
for(int i = pos + 1; i < 2 * n; ++ i) s2[++ top2] = a[i];
cnt = 1;
while(cnt <= n) {
if(s2[last2] == s1[top1] && last2 <= top2 && top1 >= last1) {
last2 ++;
ans[++ cnt] = 1 , ans[2 * n - cnt + 1] = 2;
top1 --;
}
else if(s1[last1] == s1[top1] && top1 > last1) {
last1 ++;
ans[++ cnt] = 1 , ans[2 * n - cnt + 1] = 1;
top1 --;
}
else if(s2[last2] == s2[top2] && last2 < top2) {
last2 ++;
ans[++ cnt] = 2 , ans[2 * n - cnt + 1] = 2;
top2 --;
}
else if(s1[last1] == s2[top2] && top2 >= last2 && last1 <= top1) {
last1 ++;
ans[++ cnt] = 2 , ans[2 * n - cnt + 1] = 1;
top2 --;
}
else break;
}
if(cnt == n) {
ans[1] = 2;
ans[2 * n] = 1;
for(int i = 1; i <= 2 * n; ++ i)
if(ans[i] == 1) cout << 'L';
else if(ans[i] == 2) cout << 'R';
cout << '\n';
continue;
}
cout << -1 << '\n';
}
}
\(T4\):交通规划
\(T4\) 就算了吧,我不配了。
2202.10.11 CSP-S 2021 测试总结的更多相关文章
- Noip模拟74 2021.10.11
T1 自然数 考场上当我发现我的做法可能要打线段树的时候,以为自己百分之百是考虑麻烦了 但还是打了,还过掉了所有的样例,于是十分自信的就交了 正解还真是线段树,真就第一题数据结构 但是包括自己造的小样 ...
- 2019.10.26 CSP%您赛第三场
\(CSP\)凉心模拟^_^ --题源\(lqx.lhc\)等各位蒟蒻 题目名称 比赛 传递消息 开关灯 源文件名 \(competition.cpp\) \(message.cpp\) \(ligh ...
- iOS冰与火之歌(番外篇) - 基于PEGASUS(Trident三叉戟)的OS X 10.11.6本地提权
iOS冰与火之歌(番外篇) 基于PEGASUS(Trident三叉戟)的OS X 10.11.6本地提权 蒸米@阿里移动安全 0x00 序 这段时间最火的漏洞当属阿联酋的人权活动人士被apt攻击所使用 ...
- 背水一战 Windows 10 (11) - 资源: CustomResource, ResourceDictionary, 加载外部的 ResourceDictionary 文件
[源码下载] 背水一战 Windows 10 (11) - 资源: CustomResource, ResourceDictionary, 加载外部的 ResourceDictionary 文件 作者 ...
- HP 820 G2变色龙安装10.11.6基本完美
初始状态: 一块ssd硬盘,MBR格式分区,安装了WIN7 64位. 不想动win系统,因此就安装在硬盘的扩展分区 电脑配置: cpu: i7-5600u 声卡: ALC280 显卡: HD55 ...
- MAC OS升级到10.11(OS X EICAPTION)之后CocoaPods不能正常使用的问题解决
昨晚回家之后开始升级系统到10.11,下载了一整个晚上之后终于在早上下载完毕,早上带到公司,想查一个第三方库的时候却遇到了问题: guoyufudeMacBook-Pro:~ GuoYufu$ pod ...
- 释放修改OS X 10.11系统文件权限【转】
序言:有时要替换相关的(系统目录下的)文件以完成软件的破解,但在 OS X 10.11 系统图形界面下,Root(系统超级用户)已‘转变’为 Administrator(管理员用户),选择系统文件夹( ...
- macosx 10.11 python pip install 出现错误OSError: [Errno 1] Operation not permitted:
Exception: Traceback (most recent call last): File , in main status = self.run(options, args) File , ...
- 战神Z7 D2安装黑苹果OS X El Capitan 10.11.2
安装之初状态:两块硬盘,都是MBR格式分区,一块是机械硬盘,安装了WIN7 32位和Linux,一块是SSD,安装的是WIN7 64位与WIN10 64位以前玩过Mavericks,安装的就是在硬盘的 ...
- [原创]Mac系统下制作OS 10.11安装镜像
一.所需软件 1.从App Store下载OS X El Capitan 10.11.2 ------------------------------------------------------- ...
随机推荐
- elasticsearch 排错总结
控制台乱码 修改elasticsearch-7.6.2\config下的jvm.options文件,在任意行上加上 -Dfile.encoding=GBKIK报错但成功启动,按照网上的说法是jdk权限 ...
- C盘爆红满了怎么办?既然C盘装不下了,那咱给C盘扩容不就完事了吗!
我们在使用电脑的过程中,经常容易出现C盘爆红,反而其他盘还有大量可用空间的情况. 为什么会这样呢?其实主要就两种原因: 一是电脑使用习惯不好,不管什么软件都默认安装在C盘,大文件又喜欢放在桌面,久而久 ...
- vue项目打包后,自由修改配置如接口地址等
背景描述: 项目打包后,如果想更换接口域名或者项目名称,就需要再次打包.但是这样子操作有点耗费时间.如果把这些配置写到一个文件中,然后在index.html文件中引入使用,这样子会大大提高工资效率,节 ...
- 通过系统函数分配内存sbrk/sbrk
#include <unistd.h> #include <stdio.h> int main(void) { printf("================brk ...
- libnode使用addon
自己编译的一个libnode.so后,在js里调用hello.node的 addon时候会报错. Error: dlopen failed: cannot locate symbol "na ...
- c++中内联函数和宏函数的区别
一. 区别: 是不是函数: 宏定义不是函数,但是使用起来像函数.预处理器用复制宏代码的方式代替函数的调用,省去了函数压栈退栈过程,提高了效率: 内联函数本质上是一个函数,内联函数一般用于函数体的代码比 ...
- NOI 1.7编程基础之字符串
11:潜伏者 1.描述 R国和S国正陷入战火之中,双方都互派间谍,潜入对方内部,伺机行动. 历经艰险后,潜伏于S国的R国间谍小C终于摸清了S国军用密码的编码规则: 1. S国军方内部欲发送的原信 ...
- C盘满了
今天电脑提示说C盘磁盘满了,于是开始做磁盘清理 右击C盘,点管理. 点击磁盘清理,勾选中临时文件.下载.回收站.缩略图,然后点击清理系统文件. 再去查看C盘仍然没有多大变化,于是挨个翻看C盘到底哪 ...
- 1.java 开始
WelloWorld 随便新建一个文件夹,存放代码 新建一个java文件 编写代码 编译javac java文件,生成一个class文件 运行class文件,java class 可能遇到的情况 每个 ...
- Docker的资源限制
CPU CGROUP MEM CGROUP Storage 默认的overlay fs不支持配额,需要底层文件系统如xfs,ext4的支持. 在docker中启用示例如下,限制单个容器最大使用空间为2 ...