『模拟赛』多校A层冲刺NOIP2024模拟赛12

Rank

挂了不少,还行

image

image

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。

正解仙姑,估计需要复习板子。 板子是复习不下去的,不如改改。

其实只要稍微化一下式子然后分讨就完了。

考虑原式:

\[\begin{cases}(x-1)d+y\equiv a\pmod w\\(y-1)d+x\equiv v\pmod w \end{cases} \]

上下相加相减得到:

\[\begin{cases}(x+y)(d+1)\equiv a+b+2d\pmod w\\(y-x)(d-1)\equiv b-a\pmod w \end{cases} \]

把两式子中的 \(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\)。简单想想可以得出以下结论:

image

一组解只会在区间 \([1,min(m,d)]\) 中产生,考虑某一个 \(x\) 的第一个解 \(y\) 在该区间的最左,设 \(y\) 解集的大小为 \(k\),那么在 \(x\) 减小一定范围内,解的个数都是 \(k\),即图中红线集体右移。当移动到右端点时,\(x\) 再减小,解的个数就会变为 \(k-1\)。我们考虑卡这个解个数为 \(k\)\(x\) 的区间。注意在取模意义下可能出现诸如 \(l\gt r\) 的情况,注意分好情况然后计数就做完了。

image

点击查看代码
#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 再换到本身位置的操作得来的。然后因为步数少,可以用迭代加深搜索做。所谓迭代加深搜索,其实就是规定了搜索的深度不超过某个钦定的值,相当于做了很大的剪枝。这样还不足以通过本题。我们发现每次翻转只会改变一对相邻数对,因此对于每个状态求出值不连续的相邻数对的数量,剩余步数一定大于这个值,然后就能过了。稍微卡卡常能跑的飞快。

image

点击查看代码(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 就在之后的某次更重要的比赛上复活了。

加油,我的第一次,也是最后一次。


完结撒花~

image

image

posted @ 2024-10-24 21:00  Ratio_Y  阅读(67)  评论(0编辑  收藏  举报