Codeforces Educational Round 132 赛后解题报告(已更新至 E)
Codeforces Educational Round 132 赛后解题报告
A. Three Doors
(我都会做太简单了,下一个)
B. Also Try Minecraft
(我都会做,下一个)
C. Recover an RBS
居然有笨B想着用搜索解决问题,甚至被人 hack 了。
没错我就是那个笨B。
本题的正确做法应该是思维题。对于一个括号序列 \(s\)。我们可以规定一个数组 \(a\)。满足:
我们规定 \(sum_i=\sum\limits_{i=1}^n a_i\)。
那么合法括号序列 \(s\) 满足 \(sum_n=0\) 且 \(\forall i,a_i\geq 0\)。我们通过这两个条件可以知道 ?
中有多少 (
和 )
了。为了昂 \(a_i\geq 0\)。我们应该在能填 (
时尽量填。填完后检验是否可行,我们就得到了第一组解。
如何判断是否存在第二组呢。如果存在 \(s_x={'('}\),\(s_y={')'}\)。我们交换 \(s_x,s_y\),就会使 \(a_x\) 到 \(a_y\) 全部减去 \(2\)。所以最优一定是交换最后一个填 \({'('}\) 的问号和第一个填 \({')'}\) 的问号。验证一下即可。
时间复杂度 \(O(n)\)
//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
char read_char() {
char ch=getchar();
while(!isalpha(ch)) {
ch=getchar();
}
return ch;
}
const int MAXN=2e5+10;
int n;
string s;
stack<int> stk;
vector<int> pos;
bool find(int i) {
if(i==n) {
return stk.empty();
}
if(s[i]=='(') {
stk.push(1);
return find(i+1);
}
else {
if(stk.empty()) {
return 0;
}
stk.pop();
return find(i+1);
}
}
bool check() {
while(!stk.empty()) {
stk.pop();
}
return find(0);
}
signed main() {
int t=read();
while(t--) {
pos.clear();
cin>>s;
n=s.size();
int cha=0,he=0;
for(int i=0;i<n;i++) {
if(s[i]=='?') {
he++;
pos.push_back(i);
}
if(s[i]==')') {
cha++;
}
if(s[i]=='(') {
cha--;
}
}
int x,y;
x=(cha+he)/2;
y=(he-cha)/2;
for(int i=0;i<x;i++) {
s[pos[i]]='(';
}
for(int i=x;i<he;i++) {
s[pos[i]]=')';
}
if(!check()) {
puts("NO");
continue;
}
if(he>1&&x>0&&y>0) {
swap(s[pos[x-1]],s[pos[x]]);
if(check()) {
puts("NO");
continue;
}
}
puts("YES");
}
return 0;
}
D. Rorororobot
这个题比 C 简单吧(
首先我们可以先判断 \(s_x,t_x\) 以及 \(s_y,t_y\) 是否模 \(k\) 同余。
因为没有操作次数上限,我们可以随意行动。由于所有的障碍都是在底部的,所以我们应该到达一个我们可以到达的最大高度,然后行动。这样一定最优。我们只需要再写一个 st
表求最大值即可。
//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
char read_char() {
char ch=getchar();
while(!isalpha(ch)) {
ch=getchar();
}
return ch;
}
const int MAXN=2e5+10;
int n,m,q;
int f[MAXN][20],a[MAXN];
int qry(int l,int r) {
if(r<l) {
swap(l,r);
}
int len=r-l+1;
int k=log2(len);
return max(f[l][k],f[r-(1<<k)+1][k]);
}
int main() {
cin>>n>>m;
swap(n,m);
for(int i=1;i<=n;i++) {
f[i][0]=a[i]=read();
}
for(int i=1;i<=18;i++) {
for(int j=1;j+(1<<i-1)<=n;j++) {
f[j][i]=max(f[j][i-1],f[j+(1<<i-1)][i-1]);
}
}
cin>>q;
while(q--) {
int sx,sy,tx,ty,k;
scanf("%d%d%d%d%d",&sx,&sy,&tx,&ty,&k);
swap(sx,sy);swap(tx,ty);
sy=(m-sy)/k*k+sy;
if((sx-tx)%k!=0||(sy-ty)%k!=0) {
puts("NO");
}
else if(qry(sx,tx)>=sy) {
puts("NO");
}
else {
puts("YES");
}
}
return 0;
}
E. XOR Tree
这个题很有意思。
我们发现只要修改一个节点,由于这个数可以是任意数字,可以大于 \(2^30\)。因此只要修改了这个数,所有经过这个节点的路径都可以不再考虑了。换句话说,我们可以不考虑以这个节点为根的子树了。
我们设 \(dis_u\) 为根节点到 \(u\) 路径上的异或和。
既然是涉及子树的问题,我们可以 dfs
解决。比如我们现在递归到以 \(u\) 为根节点的子树里,若子树中存在 \(x,y\),满足近这两点之间路径异或和为 \(0\),那么一定满足 \(dis_x \ \text{xor}\ dis_y=a_u\)。
所以我们的思路很明确了,对于每个节点建立一个集合,记录下这个子树中有哪些 \(dis_x\)。对于子树根节点 \(u\) 只需合并子节点的集合即可。可以用到启发式合并。合并时就可以检验 \(u\) 时候需要修改了。
如果 \(u\) 需要修改,那么这个集合就没有必要向上再合并了,原因之前说过了。