『模拟赛』多校A层冲刺NOIP2024模拟赛12
Rank
挂了不少,还行
Upd:加了张 p
A. Alice 和璀璨花
签。
一眼最长上升子序列,昨天在 AT 专题里刚见过,不过赛时没想到离散化之后树状数组,所以打的动态开点,结果细节挂了 30pts。
和最长上升子序列思路基本一致,直接区间查询 \([1,a_i-1]\) 的最大值,然后在 \(a_i\times b_{f_i}\) 插入 \(f_i\) 即可,时间空间都是 \(\mathcal{O(n\log n)}\) 的。
常数略大,稍微卡卡就过去了。几个卡常 trick:动态开点加取地址符会快很多;没事把函数都加上 inline
;剪枝尽量;快读 + printf
+ puts
;空间开够;不要 #define int long long
!
点击查看代码
// ubsan: undefined
// accoders
#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)--)
typedef long long ll;
using namespace std;
#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 pll pair<ll, ll>
#define fi first
#define se second
#define M_P(x, y) make_pair(x, y)
const int Ratio = 0;
const int N = 1e6 + 5;
int n, b[N], f[N], ans, cnt = 1;
ll a[N], maxx, MA;
int t[N * 20], son[N * 20][2];
namespace Wisadel
{
#define ls son[rt][0]
#define rs son[rt][1]
#define mid ((l + r) >> 1)
inline void Wpushup(int rt)
{
t[rt] = max(t[ls], t[rs]);
}
inline void Wadd(int &rt, ll l, ll r, ll x, int k)
{
if(!rt)rt=++cnt;
if(l == r){t[rt] = max(t[rt], k); return ;}// !
if(x <= mid)
{
Wadd(ls, l, mid, x, k);
}
else
{
Wadd(rs, mid + 1, r, x, k);
}
Wpushup(rt);
}
inline int Wq(int rt, ll l, ll r, ll x, ll y)
{
if(!rt) return 0;
if(x <= l && r <= y) return t[rt];
int ans = 0;
if(x <= mid) ans = Wq(ls, l, mid, x, y);
if(y > mid) ans = max(ans, Wq(rs, mid + 1, r, x, y));
return ans;
}
short main()
{
freopen("alice.in", "r", stdin), freopen("alice.out", "w", stdout);
n = qr;
fo(i, 1, n) a[i] = qr, MA = max(MA, a[i]);
fo(i, 1, n) b[i] = qr, maxx = max(maxx, MA * b[i]);
int rot = 1;
fo(i, 1, n)
{
f[i] = Wq(1, 1, maxx, 1, a[i] - 1) + 1;
Wadd(rot, 1, maxx, 1ll * a[i] * b[f[i]], f[i]);
ans = max(ans, f[i]);
}
printf("%d\n", ans);
return Ratio;
}
}
signed main(){return Wisadel::main();}
B. Bob 与幸运日
解同余方程 + 分讨,挺 ex 的。
暴力直接跑,不过要注意 \(a,b=w\) 的情况,直接开局取模即可,挂了 10pts。
正解仙姑,估计需要复习板子。 板子是复习不下去的,不如改改。
其实只要稍微化一下式子然后分讨就完了。
考虑原式:
上下相加相减得到:
把两式子中的 \(d\ \pm\ 1\) 移个项就能求解得到 \(x+y\) 和 \(y-x\) 的值,这样是简单的,但是若无法移项,即移项后无意义,即 \(d\ \pm\ 1\equiv 0\pmod w\) 时,我们就要特殊考虑了。
以上面的式子举例,当 \(d+1\equiv 0 \pmod w\) 时,左边一定为 0,那么要求 \(a+b+2d\equiv 0\pmod w\),否则无解。若有解,则此式恒成立,只考虑另一个式子即可。
再考虑只有一个等式的情况。此时一个 \(x\) 会对应一组解 \(y\),这些 \(y\) 在数轴上的间隔都是 \(w\)。简单想想可以得出以下结论:
一组解只会在区间 \([1,min(m,d)]\) 中产生,考虑某一个 \(x\) 的第一个解 \(y\) 在该区间的最左,设 \(y\) 解集的大小为 \(k\),那么在 \(x\) 减小一定范围内,解的个数都是 \(k\),即图中红线集体右移。当移动到右端点时,\(x\) 再减小,解的个数就会变为 \(k-1\)。我们考虑卡这个解个数为 \(k\) 时 \(x\) 的区间。注意在取模意义下可能出现诸如 \(l\gt r\) 的情况,注意分好情况然后计数就做完了。
点击查看代码
#pragma GCC optimize(3)
#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)--)
typedef long long ll;
using namespace std;
#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 pll pair<ll, ll>
#define fi first
#define se second
#define M_P(x, y) make_pair(x, y)
const int Ratio = 0;
const int N = 1e6 + 5;
ll m, d, w, a, b;
namespace Wisadel
{
inline ll Wqp(ll x, ll y)
{
ll res = 1;
x %= w;
while(y)
{
if(y & 1) res = res * x % w;
x = x * x % w;
y >>= 1;
}
return res;
}
inline bool Wno()
{
if((d - 1) % w == 0 && (a - b) % w != 0) return 0;
if((d + 1) % w == 0 && (a + b + 2 * d) % w != 0) return 0;
return 1;
}
short main()
{
freopen("bob.in", "r", stdin), freopen("bob.out", "w", stdout);
int T = qr;
while(T--)
{
m = qr, d = qr, w = qr, a = qr, b = qr;
if(!Wno()){puts("0"); continue;}
ll len = min(m, d);
ll xady = (a + b + 2 * d) % w * Wqp(d + 1, w - 2) % w;
ll yrex = (b - a + w) % w * Wqp((d - 1 + w) % w, w - 2) % w;
ll y = (xady + yrex) % w * Wqp(2, w - 2) % w, x = (xady - yrex + w) % w * Wqp(2, w - 2) % w;
if(((d - 1) % w == 0 && (b - a) % w == 0) || ((d + 1) % w == 0 && (a + b + 2 * d) % w == 0))
{
ll zc = len % w, z1 = (len - 1) / w + 1, z2 = z1 - 1;
if(!zc)
{
printf("%lld\n", z1 * len);
continue;
}
ll l, r;
if((d - 1) % w == 0 && (b - a) % w == 0) r = (xady - 1 + w) % w, l = (xady - zc + w) % w;
else r = (len - yrex + w) % w, l = (1 - yrex + w) % w;
ll tim = len / w, d1 = tim * (r >= l ? r - l + 1 : r - l + w), d2 = len;
len %= w;
if(l <= r)
{
zc = min(len, r) - max(1ll, l) + 1;
if(l > len) zc = 0;
d1 += zc;
}
else
{
d1 += min(len, r) + max(len, l) - l + 1;
if(len < l) d1--;
}
d2 -= d1;
printf("%lld\n", d1 * z1 + d2 * z2);
continue;
}
if(!y) y = w;
if(!x) x = w;
ll a1 = 0, a2 = 0;
if(len >= y) a1 = 1 + (len - y) / w;
if(len >= x) a2 = 1 + (len - x) / w;
printf("%lld\n", a1 * a2);
}
return Ratio;
}
}
signed main(){return Wisadel::main();}
C. Charlie 的运输网
有点复杂的树上问题。
暴力就是每次 \(\mathcal{O(n)}\) 跑一遍图,如果跑到了已经跑过的点就看是否符合 \(dis_v\equiv dis_u+1\pmod 2\),不符合直接 return 即可,复杂度 \(\mathcal{O(nq)}\),有 25pts。
正解仙姑。
D. David 与和谐号
迭代加深搜索。
首先一个上界 \(2n-2\) 是通过对 \(2\) ~ \(n\) 做了共 \(n-1\) 次先换到 1 再换到本身位置的操作得来的。然后因为步数少,可以用迭代加深搜索做。所谓迭代加深搜索,其实就是规定了搜索的深度不超过某个钦定的值,相当于做了很大的剪枝。这样还不足以通过本题。我们发现每次翻转只会改变一对相邻数对,因此对于每个状态求出值不连续的相邻数对的数量,剩余步数一定大于这个值,然后就能过了。稍微卡卡常能跑的飞快。
点击查看代码(41ms)
#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)--)
typedef long long ll;
using namespace std;
#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 pll pair<ll, ll>
#define fi first
#define se second
#define M_P(x, y) make_pair(x, y)
const int Ratio = 0;
const int N = 30 + 5;
int n, ans;
int a[N];
namespace Wisadel
{
inline int Wq()
{
int res = 0;
fo(i, 1, n) res += (abs(a[i] - a[i + 1]) != 1);
return res;
}
inline bool Wdo(int res)
{
int zc = Wq();
if(res + zc > ans) return 0;
if(res == ans) return !zc;
fo(i, 2, n)
{
reverse(a + 1, a + 1 + i);
if(Wdo(res + 1)) return 1;
reverse(a + 1, a + 1 + i);
}
return 0;
}
short main()
{
freopen("david.in", "r", stdin), freopen("david.out", "w", stdout);
int T = qr;
while(T--)
{
n = qr, ans = 0;
fo(i, 1, n) a[i] = qr;
a[n + 1] = n + 1;
while(!Wdo(0)) ans++;
printf("%d\n", ans);
}
return Ratio;
}
}
signed main(){return Wisadel::main();}
末
CSP 前最后一场模拟赛,紧张是肯定的,有些地方明显失常了。
T1 想剪枝想假了,发现赛时的做法会漏掉某个区间内的值,其实还是没手动算最坏的时空复杂度,每次最多加 \(\log n\) 个点,所以单点修改到底就行。T2 忘了怎么解同余方程(其实就没想到要解),暴力还挂了。T3 T4 感觉暴力也没打完,还能拿更多的分。但其实加上 T1 T2 挂的就能 Rank1 了,有点可惜。好在不是正式比赛,一切都还有机会。
考前心态最重要,没准这挂的 40pts 就在之后的某次更重要的比赛上复活了。
加油,我的第一次,也是最后一次。