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

Rank

还行

image

A. 排列最小生成树 (pmst)

签,有点可惜。

考虑连 \(i\)\(i+1\) 时,所有边边权都是小于 \(n\) 的,因此我们只考虑边权小于 \(n\) 的边即可。因为边权为 \(|p_i-p_j|\times|i-j|\),所以只考虑 \(|p_i-p_j|\lt \sqrt{n}\)\(|i-j|\lt \sqrt{n}\) 的情况,每个点只用连 \(\sqrt{n}\) 条边,复杂度为 \(\mathcal{O(n\sqrt{n}\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;
#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 pii pair<int, int>
#define P_B(x) push_back(x)
#define M_P(a, b) make_pair(a, b)
#define fi first
#define se second
const int Ratio = 0;
const int N = 5e4 + 5;
int n, cnt;
int a[N], fx[N], p[N], siz[N];
ll ans;
vector<pii> e[N];
namespace Wisadel
{
    inline int Wfind(int x)
    {
        if(x == fx[x]) return x;
        return fx[x] = Wfind(fx[x]);
    }
    short main()
    {
        freopen("pmst.in", "r", stdin) , freopen("pmst.out", "w", stdout);
        n = qr;
        fo(i, 1, n) a[i] = qr, fx[i] = i, p[a[i]] = i, siz[i] = 1;
        for(int i = 1; i * i <= n; i++) fo(j, 1, n - i)
        {
            int zc = 1ll * abs(a[j + i] - a[j]) * i;
            if(zc < n) e[zc].P_B(M_P(j, j + i));
        }
        for(int i = 1; i * i <= n; i++) fo(j, 1, n - i)
        {
            int zc = 1ll * abs(p[j] - p[j + i]) * i, zd = 1ll * abs(p[j] - p[j + i]);
            if(1ll * zd * zd > 1ll * n && zc < n) e[zc].P_B(M_P(p[j], p[j + i]));
        }
        fo(i, 1, n) if(e[i].size())
        {
            for(pii j : e[i])
            {
                int _ = Wfind(j.fi), __ = Wfind(j.se);
                if(_ != __)
                {
                    if(siz[_] >= siz[__])
                    {
                        siz[_] += siz[__];
                        fx[__] = _;
                    }
                    else
                    {
                        siz[__] += siz[_];
                        fx[_] = __;
                    }
                    ans += i;
                }
            }
        }
        printf("%lld\n", ans);
        return Ratio;
    }
}
signed main(){return Wisadel::main();}

B. 卡牌游戏 (cardgame)

真正的签,不知道跟 T2 换位置有什么深意。

发现卡牌数量互质情况下每一对牌都会 pk 一次,当不互质时,会重复 \(gcd(n,m)\) 次完全一样的对局,此时对于一张牌显然不会与其余所有牌 pk,手模发现它只会和从它开始与它间隔 \(gcd(n,m) - 1\) 个的牌 pk,然后 \(\mathcal{O(n)}\) 跑一遍就做完了。

为了方便,我分成了 \(n\le m\)\(n\gt 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;
#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 pii pair<int, int>
#define M_P(a, b) make_pair(a, b)
#define fi first
#define se second
#define P_B(x) push_back(x)
const int Ratio = 0;
const int N = 1e5 + 5, M = 5e5;
int n, m;
int a[N], b[N];
ll win, lose, ping;
vector<int> sum[N];
namespace Wisadel
{
    short main()
    {
        freopen("cardgame.in", "r", stdin) , freopen("cardgame.out", "w", stdout);
        n = qr, m = qr;
        fo(i, 1, n) a[i] = qr;
        fo(i, 1, m) b[i] = qr;
        int gcd = __gcd(n, m);
        if(n <= m)
        {
            fo(len, 1, gcd)
            {
                for(int i = len; i <= m; i += gcd)
                    sum[len].P_B(b[i]);
                sort(sum[len].begin(), sum[len].end());
            }
            fo(i, 1, n)
            {
                int zu = i % gcd; if(zu == 0) zu = gcd;
                int zc = lower_bound(sum[zu].begin(), sum[zu].end(), a[i]) - sum[zu].begin();
                zc++;
                win += 1ll * (zc - 1);
                int zd = upper_bound(sum[zu].begin(), sum[zu].end(), a[i]) - sum[zu].begin();
                zd++;
                ping += 1ll * (zd - zc);
                lose += 1ll * (m / gcd - zd + 1);
            }
            win *= gcd, ping *= gcd, lose *= gcd;
        }
        else
        {
            fo(len, 1, gcd)
            {
                for(int i = len; i <= n; i += gcd)
                    sum[len].P_B(a[i]);
                sort(sum[len].begin(), sum[len].end());
            }
            fo(i, 1, m)
            {
                int zu = i % gcd; if(zu == 0) zu = gcd;
                int zc = lower_bound(sum[zu].begin(), sum[zu].end(), b[i]) - sum[zu].begin();
                zc++;
                lose += 1ll * (zc - 1);
                int zd = upper_bound(sum[zu].begin(), sum[zu].end(), b[i]) - sum[zu].begin();
                zd++;
                ping += 1ll * (zd - zc);
                win += 1ll * (n / gcd - zd + 1);
            }
            win *= gcd, ping *= gcd, lose *= gcd;
        }
        printf("%lld\n%lld\n%lld\n", win, lose, ping);
        return Ratio;
    }
}
signed main(){return Wisadel::main();}

C. 比特跳跃 (jump)

又一道魔改最短路。

  • \(s=1\) 时,发现除了最大点为 \(11\cdots 1_{(2)}\) 之外的情况,都能找到一个点与一个数的位与值为 0,所以特判 \(n\) 即可。

  • \(s=2\) 时,发现对三点一定有 \(a\oplus b+b\oplus c=a\oplus c\),因此我们可以省掉 \(a\rightarrow c\) 这条边,即每个点只与与其一位不一样的点连边即可,每个点只有 \(\log n\) 条。

  • \(s=3\) 时,发现 \((a|b) + (b|c) \ge (a|c)\)。对于点 \(i\) 边权最少是 \(i\)。我们考虑给每个点多映射一个虚点,点到虚点的边权为 \(k\times i\),虚点到实点边权为 \(0\),然后在虚点上连边,同样只与与其一位不一样的点连边,若该点该位为 1 那么没有额外边权,否则边权为 \(k\times 2^i\)\(i\) 为位数)。这样连只多了 \(n\times(2+\log n)\) 条边,大概是最优的连法。

然后对 \(s\neq 1\) 的情况跑一遍 dijkstra 就做完了,是目前最优解。

对于 \(s=2\) 中的 \(a\oplus b+b\oplus c=a\oplus c\) 指任意三点存在这样的关系,是一种组合而不是排列。

点击查看代码
#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;
#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 pii pair<int, int>
#define M_P(a, b) make_pair(a, b)
#define fi first
#define se second
#define P_B(x) push_back(x)
const int Ratio = 0;
const int N = 2e5 + 5, M = 5e5;
int n, m, s, k;
int hh[N], to[N << 4], ne[N << 4], cnt;
ll dis[N], w[N << 4];
bool yz[N];
struct rmm
{
	ll dis; int u;
	bool operator < (const rmm &a) const {return a.dis < dis;}
};
namespace W
{
    void Wadd(int u, int v, ll val)
    {
        to[++cnt] = v;
        w[cnt] = val;
        ne[cnt] = hh[u];
        hh[u] = cnt;
    }
    void Wdij(int x)
    {
        priority_queue<rmm> q;
        memset(dis, 0x3f, sizeof dis);
        dis[x] = 0;
        q.push({0, x});
        while(q.size())
        {
            int u = q.top().u; q.pop();
            if(yz[u]) continue;
            yz[u] = 1;
            for(int i = hh[u]; i != -1; i = ne[i])
            {
                int v = to[i];
                if(dis[v] > dis[u] + w[i])
                {
                    dis[v] = dis[u] + w[i];
                    q.push({dis[v], v});
                }
            }
        }
    }
}
namespace W1
{
    short main()
    {
        fill(dis + 1, dis + 1 + n, 0);
        int zc = 1;
        while(zc <= n) zc *= 2;
        if(n == zc - 1)
        {
            dis[n] = k;
            for(int i = hh[n]; i != -1; i = ne[i])
                dis[n] = min(dis[n], w[i]);
        }
        else dis[n] = 0;
        fo(i, 2, n) printf("%lld ", dis[i]); puts("");
        return Ratio;
    }
}
namespace W2
{
    short main()
    {
        fo(i, 0, n) fo(j, 0, 16)
        {
            int v = i ^ (1 << j);
            ll val = 1ll * k * (1 << j);
            if(v <= n) W::Wadd(i, v, val);
        }
        W::Wdij(1);
        fo(i, 2, n) printf("%lld ", dis[i]); puts("");
        return Ratio;
    }
}
namespace W3
{
    short main()
    {
        fo(i, 0, n) 
        {
            fo(j, 0, 16)
            {
                int v = i ^ (1 << j);
                if(v <= n)
                {
                    ll val = ((i >> j) & 1) ? 0 : 1ll * k * (1 << j);
                    W::Wadd(i + n + 1, v + n + 1, val);
                }
            }
            W::Wadd(i, i + n + 1, 1ll * k * i);
            W::Wadd(i + n + 1, i, 0);
        }
        W::Wdij(1);
        fo(i, 2, n) printf("%lld ", dis[i]); puts("");
        return Ratio;
    }
}
namespace Wisadel
{
    short main()
    {
        freopen("jump.in", "r", stdin) , freopen("jump.out", "w", stdout);
        n = qr, m = qr, s = qr, k = qr;
        memset(hh, -1, sizeof hh);
        fo(i, 1, m)
        {
            int a = qr, b = qr, c = qr;
            W::Wadd(a, b, c), W::Wadd(b, a, c);
        }
        if(s == 1) return W1::main();
        if(s == 2) return W2::main();
        if(s == 3) return W3::main();
        return Ratio;
    }
}
signed main(){return Wisadel::main();}

D. 区间 (interval)

据说是线段树区间历史和板子。

Upd:更像是单调栈板子。

将询问按右端点升序离线,发现第一个条件相当于是 \(a_l\)\(a_i(i\in[l,r-1])\) 的最大值,维护一个单调递减的单调栈,然后二分出满足条件二的最大区间,区间中的点贡献加 1,然后删除栈内元素记得加 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;
#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 pii pair<int, int>
#define M_P(a, b) make_pair(a, b)
#define fi first
#define se second
#define P_B(x) push_back(x)
const int Ratio = 0;
const int N = 1e6 + 5, M = 5e5;
int n, m, top;
int a[N], b[N];
struct rmm{int id, l, r;} q[N];
pii st[N];
int tag[N << 2];
ll sum[N << 2], lazy[N << 2], ans[N];
namespace Wisadel
{
    #define ls (rt << 1)
    #define rs (rt << 1 | 1)
    #define mid ((l + r) >> 1)
    void Wpushdown(int rt, int l, int r)
    {
        sum[ls] += lazy[rt] * (mid - l + 1 - tag[ls]);
        sum[rs] += lazy[rt] * (r - mid - tag[rs]);
        lazy[ls] += lazy[rt], lazy[rs] += lazy[rt];
        lazy[rt] = 0;
    }
    void Wadd(int rt, int l, int r, int x, int y)
    {
        if(x <= l && r <= y)
        {
            sum[rt] += r - l + 1 - tag[rt], lazy[rt]++;
            return ;
        }
        if(lazy[rt]) Wpushdown(rt, l, r);
        if(x <= mid) Wadd(ls, l, mid, x, y);
        if(y > mid) Wadd(rs, mid + 1, r, x, y);
        sum[rt] = sum[ls] + sum[rs];
    }
    void Wdel(int rt, int l, int r, int x)
    {
        if(l == r)
        {
            tag[rt]++;
            return ;
        }
        if(lazy[rt]) Wpushdown(rt, l, r);
        if(x <= mid) Wdel(ls, l, mid, x);
        else Wdel(rs, mid + 1, r, x);
        tag[rt] = tag[ls] + tag[rs];
    }
    ll Wq(int rt, int l, int r, int x, int y)
    {
        if(x <= l && r <= y) return sum[rt];
        if(lazy[rt]) Wpushdown(rt, l, r);
        ll res = 0;
        if(x <= mid) res += Wq(ls, l, mid, x, y);
        if(y > mid) res += Wq(rs, mid + 1, r, x, y);
        return res;
    }
    short main()
    {
        freopen("interval.in", "r", stdin) , freopen("interval.out", "w", stdout);
        n = qr;
        fo(i, 1, n) a[i] = qr;
        fo(i, 2, n) b[i] = qr;
        m = qr;
        fo(i, 1, m) q[i].l = qr, q[i].r = qr, q[i].id = i;
        sort(q + 1, q + 1 + m, [](rmm &A, rmm &B){return A.r < B.r;});
        int j = 1;
        fo(i, 1, n)
        {
            int l = 1, r = top, zc = 0;
            while(l <= r)
                if(st[mid].se < b[i]) zc = mid, r = mid - 1;
                else l = mid + 1;
            if(zc) Wadd(1, 1, n, st[zc].fi, i - 1);
            while(j <= m && q[j].r == i) ans[q[j].id] = Wq(1, 1, n, q[j].l, i), j++;
            while(top && st[top].se <= a[i]) Wdel(1, 1, n, st[top].fi), top--;
            st[++top] = M_P(i, a[i]);
        }
        fo(i, 1, m) printf("%lld\n", ans[i]);
        return Ratio;
    }
}
signed main(){return Wisadel::main();}

又双叒叕回去整改了,打的不烂。只是 T3 好多性质没切,有点恼。

赛后一分钟得知 T1 建 110 条边就过了,大样例需要建 99 条,本地跑 0.95s 多,念在 OJ 速度不如本地,只建了 100 条。

下午信友队晚上 STAOI,所以现在才发。

Upd:这两场打的都还行。

尝试着跳过今天,使得 CSP 赶在我状态好的时候。

下午改 T4,改信友队题。


完结撒花~

现找的。

image

posted @ 2024-10-20 16:00  DrRatio  阅读(106)  评论(5编辑  收藏  举报