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

Rank

打成大奋了

image

A. 选彩笔(rgb)

我是彩笔

赛时完全不会啊,打了一个 25k 的贪心结果爆栈了喜提 0pts。

最大值最小,还是二分答案。二分的答案是最大差,发现值域很小,我们在 check 时可以直接枚举每个色号的最大值,统计在所选区间范围内笔数。这样就有了 \(\mathcal{O(w^3n\log w)}\) 的暴力。

考虑三维前缀和优化,\(\mathcal{O(w^3)}\) 预处理,容斥求值,然后可以在 \(\mathcal{O(w^3\log w)}\) 的复杂度下过掉了。理论算着可能会 TLE,不过不需要太卡常也能过。

点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(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 P_B(x) push_back(x)
const int Ratio = 0;
const int N = 1e5 + 5;
int n, m, ans;
struct rmm{int r, g, b, id;} p[N];
int sum[260][260][260];
namespace Wisadel
{
    ll Wq(int i, int j, int k, int mid)
    {
        ll res = sum[i][j][k];
        if(i >= mid)
        {
            if(j >= mid)
            {
                if(k >= mid) res -= sum[i - mid][j - mid][k - mid];
                res += sum[i - mid][j - mid][k];
            }
            if(k >= mid) res += sum[i - mid][j][k - mid];
            res -= sum[i - mid][j][k];
        }
        if(j >= mid)
        {
            if(k >= mid) res += sum[i][j - mid][k - mid];
            res -= sum[i][j - mid][k];
        }
        if(k >= mid) res -= sum[i][j][k - mid];
        return res;
    }
    bool Wck(int tar)
    {
        fo(i, tar - 1, 255) fo(j, tar - 1, 255) fo(k, tar - 1, 255)
            if(Wq(i, j, k, tar) >= m) return 1;
        return 0;
    }
    short main()
    {
        freopen("rgb.in", "r", stdin), freopen("rgb.out", "w", stdout);
        n = qr, m = qr;
        fo(i, 1, n) p[i].r = qr, p[i].g = qr, p[i].b = qr, sum[p[i].r][p[i].g][p[i].b]++;
        fo(i, 1, 255) fo(j, 0, 255) fo(k, 0, 255) sum[i][j][k] += sum[i - 1][j][k];
        fo(i, 0, 255) fo(j, 1, 255) fo(k, 0, 255) sum[i][j][k] += sum[i][j - 1][k];
        fo(i, 0, 255) fo(j, 0, 255) fo(k, 1, 255) sum[i][j][k] += sum[i][j][k - 1];
        int l = 0, r = 255, ans = 255;
        while(l <= r)
        {
            int mid = (l + r) >> 1;
            if(Wck(mid + 1)) r = mid - 1, ans = mid;
            else l = mid + 1;
        }
        printf("%d\n", ans);
        return Ratio;
    }
}
signed main(){return Wisadel::main();}

B. 兵蚁排序(sort)

也很水的一个题,赛时因为 T1 想假了耽误很长时间所以没多想,推出一个假性质居然能模过所有样例,但是假,所以 30pts。

先说说赛时的假做法:首先因为发现了大的数不可能往前走,所以我是倒着做的,考虑扫到的每一个数:比 \(b_i\) 大,无解,比 \(b_i\) 小,则向前找到第一个 \(b_i\),然后对整个区间排序。你会发现由于是一整块直接排序,中间许多本来符合目标序列关系的数被打乱了,因此会多判许多无解的情况。

那么根据这个错误,推出一个有正确性的做法:正着扫,然后对于当前序列向前扫到第一个等于 \(b_i\) 的值,然后一个一个交换判断是否可行。考虑这样为什么是对的:我们逐个移动保证了移动后序列仍然满足一些原序列的逆序关系,目标序列若也存在这种关系就不管,否则再交换一次即可打破逆序关系,因此这样动不会漏掉情况。

复杂度 \(\mathcal{O(Tn^2)}\)

点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(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 fi first
#define se second
#define M_P(x, y) make_pair(x, y)
#define P_B(x) push_back(x)
const int Ratio = 0;
const int N = 1e5 + 5;
int n;
int a[N], b[N];
multiset<int> st;
vector<pii> ans;
namespace Wisadel
{
    short main()
    {
        freopen("sort.in", "r", stdin), freopen("sort.out", "w", stdout);
        int T = qr;
        while(T--)
        {
            n = qr; ans.clear();
            fo(i, 1, n) a[i] = qr;
            fo(i, 1, n) b[i] = qr;
            bool bok = 0;
            fo(i, 1, n)
            {
                int zc = 0;
                fo(j, i, n) if(b[i] == a[j]){zc = j; break;}
                fu(j, zc, i + 1)
                {
                    if(a[j] < a[j - 1])
                    {
                        ans.P_B(M_P(j - 1, j));
                        swap(a[j], a[j - 1]);
                    }
                    else{bok = 1; break;}
                }
                if(bok) break;
            }
            if(bok) puts("-1");
            else
            {
                puts("0");
                printf("%d\n", ans.size());
                for(auto i : ans) printf("%d %d\n", i.fi, i.se);
            }
        }
        return Ratio;
    }
}
signed main(){return Wisadel::main();}

C. 人口局 DBA(dba)

推柿子题。

看到的第一反应就是数位 dp,然而看一会你就会发现时间空间都是假的。然后把暴力交上去有 22pts。

考虑如何简化问题。依然从数位 dp 的过程思考,当 limit=0 时,我们此后选数没有任何障碍,也就转化成了一个计数问题:共 \(a\) 位,每位上的总和要求为 \(s\),每位能取的范围为 \(\left[0,m\right)\),求方案数。考虑容斥,钦定这 \(a\) 位中有 \(k\) 位上的数 \(\ge m\),即让 \(k\) 个数 \(+m\),求解组数,此时有 \(\sum_{i=1}^a\ x_i=s-km\),等价于 \(\sum_{i=1}^a\ (x_i+1)=s-km+a\),此时满足 \(x_i+1\ge 1\),由插板法可得此时答案为 \(\binom{a}{k}\binom{s-km+a-1}{a-1}\),那么这个子问题的答案也就有了:\(\sum_{k=0}^a\ (-1)^k\binom{a}{k}\binom{s-km+a-1}{a-1}\)

我们设 \(f_a(s)=\sum_{k=0}^a\ (-1)^k\binom{a}{k}\binom{s-km+a-1}{a-1}\) 表示在无限制条件下位数为 \(a\) 要求总和为 \(s\) 的方案数,方便后续表示。

此时考虑加上 limit 的限制。由于题目要求 \(x\lt n\),所以其本身取到 \(a_i\) 的情况答案是为 0 的不考虑,那么设这一位为 \(id\) (从高到低),则其答案为 $\sum_{i=0}^{a_{id}-1}\ f_{n-id}(s-i) $。

开推:

\[\begin{aligned}Ans_{id}(s)&=\sum_{i=0}^{a_{id}-1}\ f_{n-id}(s-i)\\ &=\sum_{i=0}^{a_{id}-1}\sum_{k=0}^{n-id}\ (-1)^k\binom{n-id}{k}\binom{s-i-km+n-id-1}{n-id-1}\\&=\sum_{k=0}^{n-id}\ (-1)^k\binom{n-id}{k}\sum_{i=0}^{a_{id}-1}\binom{s-i-km+n-id-1}{n-id-1} \end{aligned} \]

我们设 \(x=a_{id}-1,y=s-km,p=n-id-1\),则后面一坨可以表示为 \(\sum_{i=0}^x\ \binom{y-i+p}{p}\)

结合杨辉三角来表示,可以将其化为:

\[\begin{aligned}\sum_{i=0}^x\ \binom{y-i+p}{p}&=\sum_{i=0}^x\ \binom{y-i+p+1}{p+1}-\binom{y-i+p}{p+1}\\&=\binom{y+p+1}{p+1}-\binom{y-x+p}{p+1} \end{aligned} \]

代回原来的式子,得:

\[Ans_{id}(s)=\sum_{k=0}^{n-id}\ (-1)^k\binom{n-id}{k}\left(\binom{s-km+n-id}{n-id}-\binom{s-km+n-id-a_{id}}{n-id}\right) \]

考虑代回原题,我们每次多固定一位等于原位上的数,那么此时答案即为 \(Ans_{n-固定的位数}(tot-sum_{固定的位数-1})\),所以总答案为 \(\sum_{i=1}^n\ Ans_{i}(tot-sum_{i-1})\),其中 \(sum_{i}\) 表示第 \(i\) 位及其高位上的数之和。预处理 \(n\times m\) 以内的阶乘和逆元,总复杂度 \(\mathcal{O(nm+n^2)}\)

点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(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 fi first
#define se second
#define M_P(x, y) make_pair(x, y)
#define P_B(x) push_back(x)
const int Ratio = 0;
const int N = 2000 + 5;
const int mod = 1e9 + 7;
int n, m;
int a[N], zc[N], tot;
ll jc[N * N], ny[N * N], ans;
namespace Wisadel
{
    ll Wqp(ll x, int y)
    {
        ll res = 1;
        while(y){if(y & 1) res = res * x % mod; x = x * x % mod; y >>= 1;}
        return res;
    }
    ll Wc(int n, int m)
    {
        if(n < m) return 0;
        return jc[n] * ny[m] % mod * ny[n - m] % mod;
    }
    short main()
    {
        freopen("dba.in", "r", stdin), freopen("dba.out", "w", stdout);
        m = qr, n = qr;
        fo(i, 1, n) a[i] = qr, tot += a[i];
        jc[0] = ny[0] = 1;
        fo(i, 1, n * m) jc[i] = jc[i - 1] * i % mod;
        ny[n * m] = Wqp(jc[n * m], mod - 2);
        fu(i, n * m - 1, 1) ny[i] = ny[i + 1] * (i + 1) % mod;
        fo(i, 1, n)
        {
            fo(k, 0, n - i)
            {
                int op = (k & 1) ? -1 : 1;
                ans = (ans + (1ll * op * Wc(n - i, k) % mod + mod) % mod * ((Wc(tot - k * m + n - i, n - i) - Wc(tot - k * m + n - i - a[i], n - i) + mod) % mod) % mod) % mod;
            }
            tot -= a[i];
        }
        printf("%lld\n", ans);
        return Ratio;
    }
}
signed main(){return Wisadel::main();}

D. 银行的源起(banking)

不会。

赛时想到找重心分开再找重心,然后发现还有居民就不会了,打了 \(\mathcal{O(n^3)}\) 暴力拿 10pts。

打的依托,T1 上来没思路就很伤,然后想了一个需要打巨大长代码的假思路更伤,结果是导致本来能切两题的场一题没切,暴力还没拿满。

学到的东西太不牢固导致一遇到需要结合多一点东西的题就发懵,维持有效状态的时间短是很重要的一个原因。

huge 晚上来开小会。内务纪律扯🥚不提,但 noip 真可能是我最后一场作为全职 OIer 的比赛了。不说像初见 OI 时那样一定冲金夺银的豪言壮语,多少也要给自己这一年多的时间一个交代,我到底有没有成长,我的实力究竟如何。挂分再怎么不应该也是挂了,没人会听你本来该能多少分,别人看到的只是最终的结果,当然,你自己真正在意的也是。所以拿出最好的状态,打一场酣畅淋漓的比赛才是最好的结果。不知道说这些能不能对自己有点影响,但是真的到了该拼的时候了,加油!


不知道会不会完结撒花~

posted @ 2024-11-05 20:10  DrRatio  阅读(70)  评论(11编辑  收藏  举报