8.22 校内模拟赛 题解报告
8.22 校内模拟赛 题解报告
扯
据说 T1 是套路题 根本不知道怎么做真是太好了
据说 T2 是手玩题 根本玩不出来真是太好了
据说 T3 是板子题 背板子被卡了真是太好了
大概这场考试就是这样...
关于考试过程以及一些题外话
这个 T1 和 T2 根本想不到啊
开考读题 看完 T1 发现只会暴力... 读到第一题的最后发现下面有一行字
额 这样的吗... 于是直接跑路
读了一下 T2 发现不太会的亚子
麻了 我信你个鬼
然后看 T3 ...
嗯... 嗯... 这波.. 这波是原题
然后开始码 四十分钟一百来行过掉了大样例
坚持个屁 前面的题还没做的 溜了溜了
于是跑去头疼前两题 爆肝 T1 无果 卑微的敲暴力... \(O(n^2)\) 滚粗
于是跑去头疼 T2 爆肝无果 卑微到连暴力都不会敲...
完了完了完了完了完了 没了没了没了没了没了
又回去看 T1
啊啊啊 还是不会 彻底完了彻底完了
不对啊 我还有 T3 我在慌什么...
T2 的五分好像可以做的亚子 默默写上...
然后别的还是不会...
好像至少可以构造出一行或一列...
要不输个 \(\max(n, m)\)
没辙了 输出来跑路了...
果然赛后被题解糊一脸 根本想不到
这里分享一个关于 T3 的故事
就是前两天的事情 在这篇博文最后也提到了
前天的时候(8.20) zxsoul 出了一道题 然后 BS 跑去写了个 CDQ 分治 被卡成 80
毕竟正解不是 CDQ 分治 树状数组的常数要好太多
于是昨天(8.21)不死心的 BS 把题目要过来进行了一波魔改 用 CDQ 跑了过去 并怂恿 zxsoul 去切题
然而 zxsoul 没有理 SB 的 BS
然后今天(8.22)上午 考试的时候又见到了这道题 (就是这套题目里面的 T3)
除了题面不一样 BS 表示将昨天的那个题目粘过来 改一下主函数就能过...
BS: "你还是逮做"(雾
得分情况
30 + 25 + 90 = 145
不知道为什么 T1 复杂度 \(O(n^2)\) 的暴力都挂成了 30 ...
不知道为什么 T2 输出 \(\max(n, m)\) 怎么骗到了 20 ...
不知道为什么 T3 背个板子都能被卡成 90 ...
题解
T1 金字塔(pyramid)
读完题想了一下 不会跑路了...
首先可以写个 \(O(n^2)\) 的混个三十到五十分
话说复杂度应该是能过五十的... 卡的莫名其妙...
关于正解
网上所找到的题解都没有给出比较详细的解释 当然这里也没有
这里只谈 BS 个人的理解 并没有什么证明 有会的神仙可以来救一下 BS
二分塔顶的值 将最底层中大于等于二分出结果的值赋为 1 其他赋为 0 返回塔顶的值
为什么可以二分 此时二分出的值相当于一个分界线 返回的塔顶的值为 1 的话 相当于大于等于该值的数可以成为最终的答案 此时如果二分出的值更小的话 不难看出 塔顶的值依旧可以为 1 也就是说答案具有单调性 所以可以二分
二分完之后怎么办
通过二分以及之后的处理 我们的序列会变成一个 01 串
通过各种手玩可以发现 若是形成的 01 串为 01 交错 那么每一层都相当于下面那一层对应位置的数取反 此时可以直接算出塔顶的数是啥
若是形成的串中出现连续的 1 或者 0 依旧是通过手玩 BS 的感觉是随着层数的增加 这一连续的数字会逐渐趋向于中间 具体的可以自己手玩一下 也是可以搞的
说不清楚 丢代码跑路了
赛事也根本就没想到二分...
代码
/*
Source: T196694 金字塔(pyramid)
*/
#include<cstdio>
/*----------------------------------------------------------*/
const int C = 1e6 + 7;
/*----------------------------------------------------------*/
int n, a[C << 1], ans;
bool b[C << 1];
/*----------------------------------------------------------*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
bool check(int x) {
for(int i = 1; n << 1 ^ i; i++) b[i] = a[i] >= x;
int l = 0, r = 0;
for(int i = 1; i ^ n; i++)
{
if(!l && b[n - i] == b[n - i + 1]) l = n - i;
if(!r && b[n + i] == b[n + i - 1]) r = n + i;
if(l && r) break;
}
if(!l && !r) return n & 1 ^ b[1];
if(!l) return b[r]; if(!r) return b[l];
return n - l < r - n ? b[l] : b[r];
}
void Main() {
n = read();
for(int i = 1; n << 1 ^ i; i++) a[i] = read();
int l = 1, r = (n << 1) - 1;
while(l <= r)
{
int mid = l + r >> 1;
if(check(mid)) ans = mid, l = mid + 1;
else r = mid - 1;
}
Print(ans);
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}
T2 演讲(speech)
据说是道手玩找规律的题
然而 BS 手玩了许久并没有玩出什么东西来 卑微骗分
关于正解
同样没有详细的题解 BS 只知道这样做确实能做 对为什么能这样做并不很清楚 有知道的大佬们可以来救一下 BS
相邻两行之间进行异或 得到一个新的矩阵 在这个新的矩阵里找最大的全 0 或全 1 矩阵即为答案
为什么可以这样做
并不是很清楚 但是通过手玩了不少数据之后 发现确实是对的
BS 的理解为 若是相邻的 0 或 1 异或之后是不影响答案的 若是 01 相间 则是可以通过画若干个十字的方法进行调整 构造出全 0 或全 1 矩阵
还是不是很明白 丢代码跑路了
代码
/*
Source: T196693 演讲(speech)
*/
#include<cstdio>
#include<bitset>
#include<cstring>
#define pt putchar(' ')
#define pn putchar('\n')
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int A = 2021;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
inline void File() {
freopen(".in", "r", stdin);
freopen(".out", "w", stdout);
}
/*----------------------------------------------------------*/
int n, m, ans, l[A], r[A], f[A][A], st[A], top;
std::bitset <A> a[A], d[A];
char s[A];
/*----------------------------------------------------------*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void Main() {
n = read(); m = read();
for(int i = 1; i ^ n + 1; i++)
{
scanf("%s", s + 1);
for(int j = 1; j ^ m + 1; j++) if(s[j] == '#') d[i][j - 1] = 1;
}
for(int i = 2; i ^ n + 1; i++) a[i] = d[i] ^ d[i - 1];
for(int i = 2; i ^ n + 1; i++)
{
f[i][m] = 1;
for(int j = m - 1; j; j--)
if(a[i][j - 1] == a[i][j]) f[i][j] = f[i][j + 1] + 1;
else f[i][j] = 1;
}
for(int j = 1; j ^ m + 1; j++)
{
top = 0;
for(int i = 2; i ^ n + 1; i++)
{
l[i] = 1;
while(top && f[st[top]][j] >= f[i][j]) l[i] += l[st[top--]];
st[++top] = i;
}
top = 0;
for(int i = n; i ^ 1; i--)
{
r[i] = 1;
while(top && f[st[top]][j] >= f[i][j]) r[i] += r[st[top--]];
st[++top] = i;
}
for(int i = 2; i ^ n + 1; i++) ans = Max(ans, f[i][j] * (r[i] + l[i]));
}
Print(ans);
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}
T3 迷宫(maze)
一上午唯一会做的一道题 还是一道板子题
然后他们都写的二维树状数组... 时间复杂度把 BS 的 CDQ 分治完爆
"我回来就把数据调成 \(10^5\) 让你们二维树状数组" 放狠话.jpg
通过分析一下 不难发现 对于每次添加围墙的操作 可以转化为将矩阵加上一个值 这样的话查询就可以转化为判断两个点的值是否相同 删除围墙的操作就是将那个值减去即可
这样问题就转化为了一道矩阵加 单点求值的问题 然后想怎么搞都可以了
然后是怎么加 加什么的问题
绝对不能加 1 的 自己手玩一下很容易就发现问题 有一个很显然的方法是随便 rand 一个值加上 把这个加的值记下来 相应的删除的时候消去即可
但是由于 BS 害怕加爆 又懒得取模什么的(但是没有想到自然溢出 其实就算想到了估计也不想写) 所以选择了异或 异或同样满足前缀 所以同样可以进行差分 不会影响正确性 消去的时候再异或一遍就好了
BS 是通过 CDQ 分治实现的 下面是代码
/*
Source: maze
*/
#include<map>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define pt putchar(' ')
#define pn putchar('\n')
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int A = 5e3 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
inline void File() {
freopen("maze.in", "r", stdin);
freopen("maze.out", "w", stdout);
}
/*----------------------------------------------------------*/
int n, m, Q, cnt, ans[B], qcnt;
struct Query {int x, y, val, id, opt;} a[B << 2], b[B << 2];
std::map <int, std::map <int, std::map <int, std::map <int, int> > > > mp;
/*----------------------------------------------------------*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
namespace TA {
#define lb(x) ((x) & -(x))
struct node {int t, val;} t[A]; int Ti;
void add(int x, int k) {
for(int i = x; i <= m; i += lb(i))
{
if(t[i].t < Ti) t[i].t = Ti, t[i].val = 0;
t[i].val ^= k;
}
}
int sum(int x) {
int res = 0;
for(int i = x; i; i -= lb(i))
{
if(t[i].t < Ti) t[i].t = Ti, t[i].val = 0;
res ^= t[i].val;
}
return res;
}
}
void solve(int l, int r) {
if(r - l <= 1) return ; int mid = l + r >> 1; solve(l, mid); solve(mid, r);
int i = l, j = mid, kcnt = l;
while(i < mid && j < r)
if(a[i].x <= a[j].x)
{
if(a[i].opt == 1) TA::add(a[i].y, a[i].val);
b[kcnt++] = a[i++];
}
else
{
if(a[j].opt == 2) ans[a[j].id] ^= TA::sum(a[j].y);
b[kcnt++] = a[j++];
}
while(i < mid) b[kcnt++] = a[i++];
while(j < r)
{
if(a[j].opt == 2) ans[a[j].id] ^= TA::sum(a[j].y);
b[kcnt++] = a[j++];
}
for(int k = l; k ^ r; k++) a[k] = b[k]; TA::Ti++;
}
void Main() {
File();
srand(20040922);
n = read(); m = read(); Q = read();
for(int i = 1; i ^ Q + 1; i++)
{
int opt = read();
if(opt == 1)
{
int x1 = read(), y1 = read(), x2 = read(), y2 = read(), val = rand();
mp[x1][y1][x2][y2] = val;
a[++cnt] = (Query){x1, y1, val, 0, 1};
a[++cnt] = (Query){x1, y2 + 1, val, 0, 1};
a[++cnt] = (Query){x2 + 1, y1, val, 0, 1};
a[++cnt] = (Query){x2 + 1, y2 + 1, val, 0, 1};
}
else if(opt == 2)
{
int x1 = read(), y1 = read(), x2 = read(), y2 = read();
int val = mp[x1][y1][x2][y2];
a[++cnt] = (Query){x1, y1, val, 0, 1};
a[++cnt] = (Query){x1, y2 + 1, val, 0, 1};
a[++cnt] = (Query){x2 + 1, y1, val, 0, 1};
a[++cnt] = (Query){x2 + 1, y2 + 1, val, 0, 1};
}
else if(opt == 3)
{
int x1 = read(), y1 = read(), x2 = read(), y2 = read(); ++qcnt;
a[++cnt] = (Query){x1, y1, 0, qcnt, 2};
a[++cnt] = (Query){x2, y2, 0, qcnt, 2};
}
}
solve(1, cnt + 1);
for(int i = 1; i ^ qcnt + 1; i++) if(!ans[i]) puts("Yes"); else puts("No");
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}
—— END