『模拟赛』NOIP2024加赛6

Rank

大奋场,T3 没切有点菜

image

A. 草莓

和前天多校 T3 很像,所以一眼鉴定为贪心,从大到小选比从小到大选一眼优,代价一样时横竖无所谓先后,然后 sort 一遍就做完了,复杂度 \((n+m)\log(n+m)\)。10min 切的。

点击查看代码
#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 fi first
#define se second
#define P_B(x) push_back(x)
#define pii pair<int , int>
const int Ratio = 0;
const int N = 2e5 + 5;
int n, m;
pii a[N << 1];
ll ans;
namespace Wisadel
{
    short main()
    {
        freopen("guiltiness.in", "r", stdin) , freopen("guiltiness.out", "w", stdout);
        n = qr, m = qr;
        int tot = n + m - 2;
        fo(i, 1, n - 1) a[i].fi = qr, a[i].se = 1;
        fo(i, 1, m - 1) a[n - 1 + i].fi = qr, a[n + i].se = 2;
        sort(a + 1, a + 1 + tot, [](pii A, pii B){return A.fi > B.fi;});
        int timn = 0, timm = 0;
        fo(i, 1, tot)
        {
            int zc = (a[i].se == 1 ? timm : timn) + 1;
            ans += 1ll * a[i].fi * zc;
            a[i].se == 1 ? timn++ : timm++;
        }
        printf("%lld\n", ans);
        return Ratio;
    }
}
signed main(){return Wisadel::main();}

为什么强调 10min 切的🤔🤔?猜猜后面 4h20min 在干嘛(

B. 三色

弱化版 ARC074E

赛时处于一种很疲倦的状态,本来不困的,但看到周围的人都困,就困了。没想到 dp 我是 fw。

\(\mathcal{O(n^3)}\) 比较显然,设 \(f_{i,j,k}\) 为到 \(k\) 时第一个与 \(k\) 颜色不一样的位置为 \(j\) 且第一个与它们颜色都不一样的位置为 \(i\) 的方案数。把限制条件归到右端点上扫描线做,这样就有了转移的限制条件。

考虑优化到 \(\mathcal{O(n^2)}\) 怎么做。把 \(i,j\) 看成平面上的两维,每次转移若不合法则相当于保留了一个矩阵,然后使其它状态置为零;否则考虑转移到 \(k+1\) 时的三种由来 \((i,j),(i,k),(j,k)\),第一种相当于向上加一层,滚掉一维后相当于不动,后两种相当于加入一行。那么考虑如何插入一行,暴力显然 \(\mathcal{O(n^2)}\),考虑维护一个 \(s_i=\sum_{j=1}^n\ f_{i,j}+f_{j,i}\) 使复杂度降到 \(\mathcal{O(n)}\),这样就实现了 \(\mathcal{O(n^2)}\) 的转移部分。

最后转移到右端点为 \(n\) 后求和即可,注意此时我们将 \(f_{i,j}\) 在行与列中分别计算了一次,所以最终结果要除以 2,注意取模。

点击查看代码
#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 fi first
#define se second
#define pii pair<int, int>
#define M_P(x, y) make_pair(x, y)
#define P_B(x) push_back(x)
const int Ratio = 0;
const int N = 1e6 + 5;
const int mod = 1e9 + 7;
int n, m;
ll s[N], zc[N];
pii d1[N], d2[N];
vector<ll> A[N], B[N], p, q, f;
namespace Wisadel
{
    short main()
    {
        freopen("color.in", "r", stdin), freopen("color.out", "w", stdout);
        int T = qr;
        while(T--)
        {
            n = qr, m = qr;
            fo(i, 0, n) d1[i] = d2[i] = M_P(0, 1e9), s[i] = 0, A[i].clear(), B[i].clear();
            f.clear(), p.clear(), q.clear();
            fo(i, 1, m)
            {
                int  l = qr, r = qr, d = qr;
                if(d == 1) d2[r].se = min(d2[r].se, l - 1);
                if(d == 2) d2[r].fi = max(d2[r].fi, l), d1[r].se = min(d1[r].se, l - 1);
                if(d == 3) d1[r].fi = max(d1[r].fi, l);
            }
            ll ans = 0;
            A[0].P_B(f.size()), B[0].P_B(f.size()), s[0] = 2, f.P_B(1);
            p.P_B(0), q.P_B(0);
            fo(i, 0, n)
            {
                fo(j, 0, i)
                {
                    if(j < d2[i].fi || j > d2[i].se)
                    {
                        for(int x : A[j]) s[p[x]] = (s[p[x]] - f[x] + mod) % mod, s[q[x]] = (s[q[x]] - f[x] + mod) % mod, f[x] = 0;
                        A[j].clear();
                    }
                    if(j < d1[i].fi || j > d1[i].se)
                    {
                        for(int x : B[j]) s[p[x]] = (s[p[x]] - f[x] + mod) % mod, s[q[x]] = (s[q[x]] - f[x] + mod) % mod, f[x] = 0;
                        B[j].clear();
                    }
                }
                if(i < n)
                {
                    fo(j, 0, i) zc[j] = s[j];
                    fo(j, 0, i) if(zc[j]) A[i].P_B(f.size()), B[j].P_B(f.size()), f.P_B(zc[j]), p.P_B(i), q.P_B(j), s[i] = (s[i] + zc[j]) % mod, s[j] = (s[j] + zc[j]) % mod;
                }
                else fo(j, 0, n) ans = (ans + s[j]) % mod;
            }
            printf("%lld\n", ans * 500000004 % mod);
        }
        return Ratio;
    }
}
signed main(){return Wisadel::main();}

C. 博弈

看到博弈,啪的一下,很快啊,就点进来了,然后发现不会做。

其实会做的,只是赛时少考虑了亿点点先手必败的局面(

设留下的数为 \(a,b,c\),令 \(a\le b\le c\)

已知当 \(c-a\le 1\) 时先手必败,令此时三个数为 \(tar(target)\)。那么一个结论是,同一时刻 \(tar\) 的数量不可能为 2。

有什么用呢?来证三者互不相等时一定先手必胜。考虑当 \(b\in tar\) 时,此时一定能一步操作将 \(a,c\) 都变成 \(tar\),所以是必胜局面。那么三者不存在 \(tar\) 时,我们可以先手一步将 \(a\leftarrow b\),那么此时局面形如 \(d,d,e\)。已知同一时刻 \(tar\) 数量不为 2,那么我们后手将状态置为此一定合法,此时对手只能操作 \(d,e\),总存在一时刻会使得 \(d\in tar\),那么局面形如上述 \(b\in tar\),先手必胜。得证。

那么考虑三者有相等时怎么做。比较显然三者都相等时先手必败,故只考虑 \(a,a,b\) 的情况。已知三者互不相等时必胜,正常人一定不会将必胜策略拱手让人,故操作时一定会尽力使得场面变为形如 \(a,b,b\) 的情况,那么不能操作达到此情况的一方输。发现此时每次操作都是将 \(a,b\) 的距离除以二,先手赢这个除以二次数一定为奇,那么得到结论:若 \(b-a\) 除以二为整数的次数为奇则先手胜。

研究下这是什么,容易想到 lowbit 位数为偶时必胜。

然后考虑实现。由于用到 lowbit 为某值时的个数,所以倒着建一棵 trie,读入时插入每个数,排序后扫一遍,简单容斥 \(ans=sum-合法\),然后就做完了。复杂度 \(\mathcal{O(n\log V)}\)

点击查看代码
#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 fi first
#define se second
#define P_B(x) push_back(x)
#define pii pair<int, int>
const int Ratio = 0;
const int N = 5e5 + 5;
int n, tot;
ll a[N];
ll C[N][4];
int t[N << 6][2], cnt[N << 6];
namespace Wisadel
{
    inline void Wclear(int x){t[x][0] = t[x][1] = cnt[x] = 0;}
    inline void Wins(ll x)
    {
        int now = 0;
        fo(i, 0, 60)
        {
            int to = (x >> i) & 1;
            if(!t[now][to]) t[now][to] = ++tot, Wclear(tot);
            now = t[now][to], cnt[now]++;
        }
    }
    inline int Wq(ll x)
    {
        int now = 0, res = 0;
        fo(i, 0, 60)
        {
            int to = (x >> i) & 1;
            if(i & 1) res += cnt[t[now][to ^ 1]];
            now = t[now][to];
        }
        return res;
    }
    short main()
    {
        freopen("game.in", "r", stdin), freopen("game.out", "w", stdout);
        int T = qr;
        C[1][1] = C[2][2] = C[3][3] = C[3][0] = 1;
        C[2][1] = 2, C[3][1] = C[3][2] = 3;
        fo(i, 4, N - 5)
        {
            C[i][0] = 1;
            fo(j, 1, 3) C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
        }
        while(T--)
        {
            n = qr; tot = 0;
            Wclear(0);
            fo(i, 1, n) a[i] = qr, Wins(a[i]);
            sort(a + 1, a + 1 + n);
            a[n + 1] = -1;
            ll ans = C[n][3];
            int num = 0;
            fo(i, 1, n)
            {
                num++;
                if(a[i] != a[i + 1])
                {
                    ans -= C[num][3] + C[num][2] * (n - num - Wq(a[i]));
                    num = 0;
                }
            }
            printf("%lld\n", ans);
        }
        return Ratio;
    }
}
signed main(){return Wisadel::main();}

D. 后缀数组

不会。暴力也不会。

开学第一场,大家的状态和放假前那一场一样好啊!

赛时小睡了一会,然后 T3 疯狂漏容斥,然后 T2 T4 疯狂不会,然后结束了。

睡着了,呼~ 呼~


完结撒花~

醒了。

image

posted @ 2024-11-18 21:40  DrRatio  阅读(64)  评论(1编辑  收藏  举报