『模拟赛』NOIP2024模拟1(更新 T2 T4)

Rank

有点可惜,

image

A. 玩游戏

绝妙贪心题。感觉这种能产生很多假做法且都可 hack 的贪心都是好题。

赛时不知道为什么犯唐没交一开始的暴力贪心。

考虑双指针,设左右指针分别为 \(l,r\)。主要思路是实时维护当前两个指针向两边最近的一个区间和不为正的段,记录该区间的和 \(sum1/sum2\) 以及中途和的最大值 \(max1/max2\),并记录另一个端点 \(ii/jj\),以及维护当前区间的和 \(sum=\sum_{i=l}^r\ a_i\)

首先考虑能否向两边移动,比较平凡,满足 \(sum+max1/max2\le 0\) 即可(注意前提是这个区间存在)。如果直接能移动到 \([1,n]\) 说明合法,直接返回 true,如果无法移动说明不合法,直接返回 false。

然后就来到了这个算法的精髓:将指针移至 \([1,n]\),从两边向中间,不断减去某个区间和,判断是否能到达当前 \([l,r]\)。维护变量相同,思路也相同,判断变为减去某个区间后是否保持和 \(\le 0\),如果能移动到 \([l,r]\) 说明合法,否则不合法。

至此判断结束,因为不会有回跳的操作,复杂度是严格 \(\mathcal{O(n)}\) 的,轻松最优解。

点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(register int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(register int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define lx ll
inline lx qr()
{char ch = getchar(); lx x = 0, f = 1;for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ 48);return x * f;
}
#undef lx
#define qr qr()
#define fi first
#define se second
#define pii pair<int, int>
#define P_B(x) push_back(x)
#define M_P(x, y) make_pair(x, y)
const int Ratio = 0;
const int N = 1e5 + 5;
int n, k;
ll a[N];
namespace Wisadel
{bool Wsol(){int l = k + 1, r = k;ll sum = 0, sum1 = 0, sum2 = 0;ll m1 = -1e18, m2 = -1e18;int ii, jj;fu(i, l - 1, 0){sum1 += a[i];m1 = max(m1, sum1);if(sum1 <= 0){ii = i; break;}}fo(i, r + 1, n + 1){sum2 += a[i];m2 = max(m2, sum2);if(sum2 <= 0){jj = i; break;}}while(1){if(l == 1 && r == n) return 1;if(sum + m1 <= 0 && ii){sum += sum1, l = ii;sum1 = 0, m1 = -1e18;fu(i, l - 1, 0){sum1 += a[i];m1 = max(m1, sum1);if(sum1 <= 0){ii = i; break;}}}else if(sum + m2 <= 0 && jj <= n){sum += sum2, r = jj;sum2 = 0, m2 = -1e18;fo(i, r + 1, n + 1){sum2 += a[i];m2 = max(m2, sum2);if(sum2 <= 0){jj = i; break;}}}else break;}if(l > r) return 0;sum = 0;fo(i, 1, n) sum += a[i], a[i] = -a[i];if(sum > 0) return 0;sum1 = sum2 = 0;m1 = m2 = a[l] = a[r] = -1e18;int L = 0, R = n + 1;fo(i, L + 1, l){sum1 += a[i];m1 = max(m1, sum1);if(sum1 <= 0){ii = i; break;}}fu(i, R - 1, r){sum2 += a[i];m2 = max(m2, sum2);if(sum2 <= 0){jj = i; break;}}while(1){if(L + 1 == l && R - 1 == r) return 1;if(sum + m1 <= 0 && ii != l){sum += sum1, L = ii;sum1 = 0, m1 = -1e18;fo(i, L + 1, l){sum1 += a[i];m1 = max(m1, sum1);if(sum1 <= 0){ii = i; break;}}}else if(sum + m2 <= 0 && jj != r){sum += sum2, R = jj;sum2 = 0, m2 = -1e18;fu(i, R - 1, r){sum2 += a[i];m2 = max(m2, sum2);if(sum2 <= 0){jj = i; break;}}}else break;}return 0;}short main(){int T = qr;while(T--){n = qr, k = qr;fo(i, 0, n - 1) a[i] = qr;n--, k--;a[0] = a[n + 1] = -1e18;puts(Wsol() ? "Yes" : "No");}return Ratio;}
}
signed main(){return Wisadel::main();}

B. 排列

好 dp,笛卡尔树,先润。

\(\mathcal{O(2^n)}\) 枚举排列 + \(k=1\) 时打表发现答案为 \(2^{n-1}\),可以得到共 50pts。

好的,学到了不需要笛卡尔树的做法,神奇 dp。设 \(f_{i,j,0/1,0/1}\) 表示长度为 \(i\),至多操作 \(j\) 次变为一个,左/右边界外是否存在大于该区间内最大数的数的区间个数。首先恰好转不超过的容斥已经很套路了,其次这样设置状态可以很好地进行转移操作。

我们再枚举 \(i,j\) 后,再枚举区间内的一个拼接点,通过分讨拼接点与两侧区间的值大小关系来转移。

  • \(k\) 为整个序列最大值,比较一眼,有:

\[f_{i,j,0,0}=\sum_{k=1}^i\ f_{k-1,j,0,1}\times f_{i-k,j,1,0}\times \binom{i-1}{k-1} \]

  • \(k\) 为右区间最大值,在左区间左侧还有更大的值,此时左区间需要在 \(j-1\) 次内消完,留一次消最大值 \(k\)

\[f_{i,j,1,0}=\sum_{k=1}^i\ f_{k-1,j-1,1,1}\times f_{i-k,j,1,0}\times \binom{i-1}{k-1} \]

  • 同上,更大值在右边:

\[f_{i,j,0,1}=\sum_{k=1}^i\ f_{i-1,j,0,1}\times f_{i-k,j-1,1,1}\times \binom{i-1}{k-1} \]

  • 左右区间两侧都有比 \(k\) 大的值,此时左右任意一区间在 \(j-1\) 次内消完即可,容斥除去两区间都要 \(j\) 次消完的方案,有:

\[f_{i,j,1,1}=\sum_{k=1}^i\ (f_{k-1,j,1,1}\times f_{i-k,j,1,1}-(f_{k-1,j,1,1}-f_{k-1,j-1,1,1})\times(f_{i-k,j,1,1}-f_{i-k,j-1,1,1}))\times \binom{i-1}{k-1} \]

由于 \(m\gt n\) 时答案一直为 0,所以复杂度是 \(\mathcal{O(n^2\log n)}\) 的。

点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(register int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(register int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define lx ll
inline lx qr()
{char ch = getchar(); lx x = 0, f = 1;for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ 48);return x * f;
}
#undef lx
#define qr qr()
#define fi first
#define se second
#define pii pair<int, int>
#define P_B(x) push_back(x)
#define M_P(x, y) make_pair(x, y)
const int Ratio = 0;
const int N = 1000 + 5;
int n, m, mod;
ll f[N][N][2][2], c[N][N];
namespace Wisadel
{short main(){n = qr, m = qr, mod = qr;fo(i, 0, n){c[i][0] = 1;fo(j, 1, i) c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;}fo(i, 0, m) f[0][i][1][1] = f[0][i][1][0] = f[0][i][0][1] = f[0][i][0][0] = 1;fo(i, 1, n) fo(j, 1, m) fo(k, 1, i){f[i][j][0][0] = (f[i][j][0][0] + f[k - 1][j][0][1] * f[i - k][j][1][0] % mod * c[i - 1][k - 1] % mod) % mod;f[i][j][1][0] = (f[i][j][1][0] + f[k - 1][j - 1][1][1] * f[i - k][j][1][0] % mod * c[i - 1][k - 1] % mod) % mod;f[i][j][0][1] = (f[i][j][0][1] + f[k - 1][j][0][1] * f[i - k][j - 1][1][1] % mod * c[i - 1][k - 1] % mod) % mod;f[i][j][1][1] = (f[i][j][1][1] + (f[k - 1][j][1][1] * f[i - k][j][1][1] % mod - (f[k - 1][j][1][1] - f[k - 1][j - 1][1][1] + mod) % mod * (f[i - k][j][1][1] - f[i - k][j - 1][1][1] + mod) % mod) % mod * c[i - 1][k - 1] % mod) % mod;}printf("%lld\n", (f[n][m][0][0] - f[n][m - 1][0][0] + mod) % mod);return Ratio;}
}
signed main(){return Wisadel::main();}

C. 最短路

暴力 dfs 20pts。

学了学长的神奇做法,二维 dijkstra。双向建图,设 \(dis_{i,j}\) 表示正向图到 \(x\) 反向图到 \(j\) 的最小代价,答案即为 \(dis_{n,n}\),记录经过的城市可以用 bitset 压一下,很好理解。由于转移经过城市是动态的,所以正确性是有保证的,而且这样做正反会保证经过图中每一个点,所以不会漏情况。时间复杂度 \(\mathcal{O(m\log m)}\)

点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(register int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(register int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define lx ll
inline lx qr()
{char ch = getchar(); lx x = 0, f = 1;for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ 48);return x * f;
}
#undef lx
#define qr qr()
#define fi first
#define se second
#define pii pair<int, int>
#define P_B(x) push_back(x)
#define M_P(x, y) make_pair(x, y)
const int Ratio = 0;
const int N = 250 + 5, M = 250 * 250 + 5;;
int n, m, ans;
int val[N], dis[N][N];
int pp[N], ot[M], en[M], tnc;
int hh[N], to[M], ne[M], cnt;
bool yz[N][N];
bitset<N> b[N][N];
struct rmm
{int d, x, y;bool operator < (const rmm &A) const{return d > A.d;}
};
namespace Wisadel
{void Wadd(int u, int v){to[++cnt] = v;ne[cnt] = hh[u];hh[u] = cnt;}void Wadd_(int u, int v){ot[++tnc] = v;en[tnc] = pp[u];pp[u] = tnc;}void Wdij(){priority_queue<rmm> q;memset(dis, 0x3f, sizeof dis);dis[1][1] = val[1];b[1][1][1] = 1;q.push({dis[1][1], 1, 1});while(q.size()){int u1 = q.top().x, u2 = q.top().y;q.pop();if(yz[u1][u2]) continue;yz[u1][u2] = 1;for(int i = hh[u1]; i != -1; i = ne[i]){int v = to[i], zc = 0;if(!b[u1][u2][v]) zc = val[v];if(dis[v][u2] > dis[u1][u2] + zc){b[v][u2] = b[u1][u2];b[v][u2][v] = 1;dis[v][u2] = dis[u1][u2] + zc;q.push({dis[v][u2], v, u2});}}for(int i = pp[u2]; i != -1; i = en[i]){int v = ot[i], zc = 0;if(!b[u1][u2][v]) zc = val[v];if(dis[u1][v] > dis[u1][u2] + zc){b[u1][v] = b[u1][u2];b[u1][v][v] = 1;dis[u1][v] = dis[u1][u2] + zc;q.push({dis[u1][v], u1, v});}}}}short main(){n = qr, m = qr;memset(hh, -1, sizeof hh);memset(pp, -1, sizeof pp);fo(i, 1, n) val[i] = qr;fo(i, 1, m){int a = qr, b = qr;Wadd(a, b);Wadd_(b, a);}Wdij();printf("%d\n", dis[n][n] == dis[0][0] ? -1 : dis[n][n]);return Ratio;}
}
signed main(){return Wisadel::main();}

D. 矩形

感觉本场比赛最简单的

平面矩形与交有关,很容易联想到扫描线。记录矩形的纵边,在纵轴上建一棵线段树,维护区间的所属块。每遇到一条边先查询该段上的块,并在查询过程中合并遇到的所有块,比较好想,如图:

image

然后插入/删除该边。插入比较好写,因为已经把区间上所有块都合并了,所以直接覆盖即可。删除需要考虑的多一些,因此我们考虑多记一个信息:每区间上整体段的数量。删除时直接使这个值减一即可,只有在这一区间上存在整体段时才可以用该段的所属块信息。意会一下。

然后是复杂度问题,注意到某区间上没有整体段时我们不取其信息,所以在查询时会递归得更深,但是复杂度是正确的,因为我们在查询时会合并途径的所有块,这样的操作最多是 \(n\) 次的,因此均摊不会影响整体复杂度。所以时间复杂度依然是 \(\mathcal{O(n\log n)}\) 的,可能常数略大,但足以薄纱题解给的双 \(\log\) 做法。

点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(register int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(register int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define lx ll
inline lx qr()
{char ch = getchar(); lx x = 0, f = 1;for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ 48);return x * f;
}
#undef lx
#define qr qr()
#define fi first
#define se second
#define pii pair<int, int>
#define P_B(x) push_back(x)
#define M_P(x, y) make_pair(x, y)
const int Ratio = 0;
const int N = 1e5 + 5;
int n, tot;
int fx[N];
struct rmm{int lx, ly, rx, ry; } b[N];
struct edge{int x, l, r, id, op; } e[N << 1];
struct sgt{int num = 0, v = 0, all = 0; } t[N << 2];
int lazy[N << 2];
namespace Wisadel
{inline int Wfind(int x){if(x == fx[x]) return x;return fx[x] = Wfind(fx[x]);}#define ls (rt << 1)#define rs (rt << 1 | 1)#define mid ((l + r) >> 1)inline sgt Wpushup(int rt, sgt a, sgt b, int op){sgt zc = {0, 0, t[rt].all};zc.num = a.num + b.num;if(a.v == b.v) zc.v = a.v;if(op){if(!a.v && !b.v) zc.v = 0;else if(!a.v) zc.v = b.v;else if(!b.v) zc.v = a.v;else{int _ = Wfind(a.v), __ = Wfind(b.v);fx[__] = _;zc.v = _;}}return zc;}inline void Wpushdown(int rt, int l, int r){if(lazy[rt] > 0){t[ls].v = t[rs].v = t[rt].v;t[ls].all += lazy[rt], t[rs].all += lazy[rt];t[ls].num += (mid - l + 1) * lazy[rt];t[rs].num += (r - mid) * lazy[rt];lazy[ls] += lazy[rt], lazy[rs] += lazy[rt];lazy[rt] = 0;}else{t[ls].all += lazy[rt], t[rs].all += lazy[rt];if(!t[ls].all) t[ls].v = 0;if(!t[rs].all) t[rs].v = 0;t[ls].num += (mid - l + 1) * lazy[rt];t[rs].num += (r - mid) * lazy[rt];lazy[ls] += lazy[rt], lazy[rs] += lazy[rt];lazy[rt] = 0;}}inline sgt Wq(int rt, int l, int r, int x, int y){if(x <= l && r <= y && t[rt].v) return t[rt];if(lazy[rt]) Wpushdown(rt, l, r);sgt zc = {0, 0, 0};if(x <= mid && t[ls].num) zc = Wpushup(rt, zc, Wq(ls, l, mid, x, y), 1);if(y > mid && t[rs].num) zc = Wpushup(rt, zc, Wq(rs, mid + 1, r, x, y), 1);return zc;}inline void Wupd(int rt, int l, int r, int x, int y, int op, int id){if(x <= l && r <= y){if(op == 1){t[rt].v = id;t[rt].all++;t[rt].num += (r - l + 1);lazy[rt]++;}else{t[rt].all--;if(!t[rt].all) t[rt].v = 0;t[rt].num -= (r - l + 1);lazy[rt]--;}return ;}if(lazy[rt]) Wpushdown(rt, l, r);if(x <= mid) Wupd(ls, l, mid, x, y, op, id);if(y > mid) Wupd(rs, mid + 1, r, x, y, op, id);t[rt] = Wpushup(rt, t[ls], t[rs], 0);}short main(){n = qr;int L = 1000000, R = 0;fo(i, 1, n){fx[i] = i;b[i].lx = qr, b[i].ly = qr, b[i].rx = qr, b[i].ry = qr;L = min(L, b[i].ly), R = max(R, b[i].ry);e[++tot] = {b[i].lx, b[i].ly, b[i].ry, i, 1};e[++tot] = {b[i].rx, b[i].ly, b[i].ry, i, 0};}sort(e + 1, e + 1 + tot, [](edge A, edge B){return A.x == B.x ? A.op > B.op : A.x < B.x;});fo(i, 1, tot){sgt zc = Wq(1, L, R, e[i].l, e[i].r);if(zc.v != 0){int _ = Wfind(zc.v), __ = Wfind(e[i].id);fx[__] = _;}Wupd(1, L, R, e[i].l, e[i].r, e[i].op, e[i].id);}int ans = 0;fo(i, 1, n) if(i == Wfind(i)) ans++;printf("%d\n", ans);return Ratio;}
}
signed main(){return Wisadel::main();}

这场有点突然,导致做 T1 时脑子极其不清醒,到结束也没有一个很好的办法,最后秉持着打假做法没意义的思想交了个随机数上去。

T2 暴力很好拿,无需多言,T3 是先看的,打了暴力就没多想,唯一很好的是开 T4 开的很早,并且很快有了思路,可惜为了卡常漏了一个 corner case 没有达成赛时切 T4 的壮举。

比较还行吧,吃了个教训,贪心假的也要写,谁知道水数据能让你拿多少分?


未完待续

image

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/826326.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

西电校园网基于 PPPoE+DHCP 方式实现带宽叠加

前言 由于个人对宿舍内局域网有一定需求,于是考虑在宿舍内配置一台路由器来搭建局域网。 而又因为西电的校园网有如下一些性质:一个免费账号可以同时在线 3 台设备,每台设备限速 100M。 校园无线网通过深澜 Portal 认证方式在设备连接到校园网 AP 且在网页端登录后以 DHCP 方…

博客园美化-Awescnb主题

本文主要记录安装Awescnb皮肤的安装过程,以及我做的配置,作为备份。一、安装皮肤 安装超级简单,根据官方文档,一分钟就搞定。首页HTML: <div id="loading"><div class="loader-inner"></div></div>页面定制CSS: #loading{bo…

数据库实验三:创建和管理数据表

数据库实验三:创建和管理数据表 惠州学院 《数据库应用》课程实验报告 实验题目: 实验三:创建与管理数据库 姓名: 曹锐旋 学号: 230703030 班级: 23 电子信息工程(3)班 指导教师: 黄冲 ‍ 一、实验目的掌握 SQL Server 中使用 T-SQL 语句创建…

【vjudge训练记录】11月个人训练赛1

训练情况赛后反思 被小数据背刺了,吃了几发RE,不过还是调出来了 A题 我们先考虑将连续的 v 先换成 w,之后就是统计子序列 wow 的个数,我们只需要找每个 o 前面有多少个 w,之后有多少个 w,根据乘法原理可知,这个 o 对答案的贡献就是两个相乘,维护前面和后面的 w 我们可以…

数据采集和融合技术作业3

作业①: 1)指定一个网站,爬取这个网站中的所有的所有图片,例如:中国气象网(http://www.weather.com.cn)。使用scrapy框架分别实现单线程和多线程的方式爬取。 代码解析 weather_spiders.py文件 解析起始页面 def parse(self, response):urls = response.xpath(//div[@cla…

19-操作系统安全保护

19.1 概述 1)概念 一般来说,操作系统的安全是指满足安全策略要求,具有相应的安全机制及安全功能,符合特定的安全标准,在一定约束条件下,能够抵御常见的网络安全威胁,保障自身的安全运行及资源安全。 操作系统的安全可控目标分为两个层面:第一个层面,是指给定一个操作系…

2024-2025-1 20241313刘鸣宇《计算机基础与程序设计》第六周工作总结

作业信息这个作业属于哪个课程 <班级的链接>(如2024-2025-1-计算机基础与程序设计)这个作业要求在哪里 <作业要求的链接>(如2024-2025-1计算机基础与程序设计第一周作业)这个作业的目标 <写上具体方面>作业正文 ... 本博客链接教材学习内容总结 学习了有关…

mysql 5.7.x版本查看某张表、库的大小 思路方案说明

mysql 5.7.x版本查看某张表、库的大小 思路方案说明@目录摘要(推荐)第一种方案:查询information_schema.TABLES的字段DATA_LENGTH使用场景优点:网上大多案例都是查询系统表的字段DATA_LENGTH去计算大小缺点:DATA_LENGTH不都是实时更新的,由表引擎决定,同时DATA_LENGTH是…

Fluent Editor 富文本开源2个月的总结:增加格式刷、截屏、TypeScript 类型声明等新特性

你好,我是 Kagol,个人公众号:前端开源星球。 Fluent Editor 是一个基于 Quill 2.0 的富文本编辑器,在 Quill 基础上扩展了丰富的模块和格式,框架无关、 功能强大、开箱即用。 2024年8月12日,Fluent Editor 正式开源!源码:https://github.com/opentiny/fluent-editor/ 官…

JavaScript DOM

一 获取Element对象点击查看代码 Document对象中提供了以下获取 Element元素对象的函数 getElementById():根据id属性值获取,返回单个Element对象 getElementsByTagName():根据标签名称获取,返回Element对象数组 getElementsByName():根据name属性值获取,返回Element对象…

zblog列表页面包屑导航的代码 支持显示所有子分类

当前位置:<a href="{$host}">网站首页</a> {if $type==category} {php} $html=; function navcate($id){global $html;$cate = new Category;$cate->LoadInfoByID($id);$html = > <a href=".$cate->Url." title="查看.$cat…

zblog注册插件调用自定义模板的方法

操作步骤安装注册插件:安装官方提供的注册插件。修改插件文件:打开/zb_users/plugin/RegPage/include.php文件。替换模板名称:在第213行,将$article->Template改为自定义模板的名称,例如login。扫码添加技术【解决问题】专注中小企业网站建设、网站安全12年。熟悉各种C…