『模拟赛』NOIP2024加赛1
Rank
有点可惜,
A. 玩游戏
绝妙贪心题。感觉这种能产生很多假做法且都可 hack 的贪心都是好题。
赛时不知道为什么犯唐没交一开始的暴力贪心。
考虑双指针,设左右指针分别为
首先考虑能否向两边移动,比较平凡,满足
然后就来到了这个算法的精髓:将指针移至
至此判断结束,因为不会有回跳的操作,复杂度是严格
点击查看代码
#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,笛卡尔树,先润。
好的,学到了不需要笛卡尔树的做法,神奇 dp。设
我们再枚举
为整个序列最大值,比较一眼,有:
为右区间最大值,在左区间左侧还有更大的值,此时左区间需要在 次内消完,留一次消最大值 :
- 同上,更大值在右边:
- 左右区间两侧都有比
大的值,此时左右任意一区间在 次内消完即可,容斥除去两区间都要 次消完的方案,有:
由于
点击查看代码
#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。双向建图,设
点击查看代码
#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. 矩形
感觉本场比赛最简单的
平面矩形与交有关,很容易联想到扫描线。记录矩形的纵边,在纵轴上建一棵线段树,维护区间的所属块。每遇到一条边先查询该段上的块,并在查询过程中合并遇到的所有块,比较好想,如图:
然后插入/删除该边。插入比较好写,因为已经把区间上所有块都合并了,所以直接覆盖即可。删除需要考虑的多一些,因此我们考虑多记一个信息:每区间上整体段的数量。删除时直接使这个值减一即可,只有在这一区间上存在整体段时才可以用该段的所属块信息。意会一下。
然后是复杂度问题,注意到某区间上没有整体段时我们不取其信息,所以在查询时会递归得更深,但是复杂度是正确的,因为我们在查询时会合并途径的所有块,这样的操作最多是
Upd:在 QED 的要求疑问下改了下,加了一个 tag 记录合并情况,直接快了一倍左右
点击查看代码
#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 的壮举。
比较还行吧,吃了个教训,贪心假的也要写,谁知道水数据能让你拿多少分?
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探