闲话 11.25

  • 上午

vp NOIP 2020。

太菜了,就会前三道(。

排水系统

签。

第一眼还以为是网络流,后来仔细一看发现就是个小水题。

由于保证了不存在环和重边,流向是一定的且与时间无关,那么容易想到对每个点分别考虑,做一遍拓扑排序就结束了。

关键点在于如何输出分数。赛场上实现一个分数类是不现实的,因此考虑用精度高一些的类型焯过去。__int128 就是一个很好的选择,每次加水时注意一些 \(\gcd\)\(\operatorname{lcm}\) 的实现细节就没了。

点击查看代码
#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;
typedef __int128 lll;
typedef long double ld;
typedef unsigned long long ull;
#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 pii pair<int, int>
#define ppp pair<pii, pii>
#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 = 5e5 + 5;
const int mod = 1e8;
int n, m;
int rd[N], cd[N];
int hh[N], to[N], ne[N], cnt;
lll fz[N], fm[N];
namespace Wisadel
{
    inline void Wadd(int u, int v)
    {
        to[++cnt] = v;
        ne[cnt] = hh[u];
        hh[u] = cnt;
    }
    inline void Wtopsort()
    {
        queue<int> q;
        fo(i, 1, n) if(!rd[i]) q.push(i), fz[i] = fm[i] = 1;
                    else fm[i] = 1;
        while(q.size())
        {
            int u = q.front(); q.pop();
            if(!cd[u]) continue;
            lll zfz = 0, zfm = 0;
            if(fz[u] % cd[u] == 0) zfz = fz[u] / cd[u], zfm = fm[u];
            else zfz = fz[u], zfm = fm[u] * cd[u];
            for(int i = hh[u]; i != -1; i = ne[i])
            {
                int v = to[i];
                lll zc = __gcd(zfm, fm[v]);
                zc = zfm * fm[v] / zc;
                fz[v] *= zc / fm[v], fm[v] = zc;
                lll zcc = zfz * zc / zfm;
                fz[v] += zcc;
                zc = __gcd(fz[v], fm[v]);
                fz[v] /= zc, fm[v] /= zc;
                rd[v]--;
                if(!rd[v]) q.push(v);
            }
        }
    }
    int st[N], top;
    inline void Wprint(lll x)
    {
        if(x == 0) putchar('0');
        else
        {
            while(x) st[++top] = x % 10, x /= 10;
            while(top) putchar(st[top--] + '0');
        }
    }
    short main()
    {
        n = qr, m = qr;
        memset(hh, -1, sizeof hh);
        fo(i, 1, n)
        {
            cd[i] = qr;
            fo(j, 1, cd[i])
            {
                int x = qr;
                Wadd(i, x);
                rd[x]++;
            }
        }
        Wtopsort();
        fo(i, 1, n) if(!cd[i])
            Wprint(fz[i]), putchar(' '), Wprint(fm[i]), puts("");
        return Ratio;
    }
}
signed main(){return Wisadel::main();}
// All talk and never answer

字符串匹配

简单计算复杂度发现暴力可行,直接上。

考虑枚举每一个 \(AB\),然后调和级数找所有满足 \((AB)^i\) 的串,哈希轻松做。那么问题就来到了奇数个数的限制。

考虑直接维护关于奇数字符个数的前缀和与后缀和。考虑到每一个 \(AB\)\(A\) 的取值区间为 \([1,|AB|-1]\),我们直接枚举右端点,那么到某一位时我们可以取到其前面所有的贡献,一路加过去即可。然后对于一个合法的 \((AB)^i\),直接计算 \(C\) 中奇数次字符的个数对应的贡献即可。时间复杂度略高 \(\mathcal{O(Tn(\log n+26))}\)

点击查看代码
#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;
typedef unsigned long long ull;
#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 P_B(x) push_back(x)
#define M_P(x, y) make_pair(x, y)
const int Ratio = 0;
const int N = (1 << 20) + 5;
const int bas = 233;
int n;
string s;
int b[N], zj[N], hj[N], cnt[N];
ull has[N], p[N];
namespace Wisadel
{
    inline ull Wgh(int l, int r){return has[r] - has[l - 1] * p[r - l + 1];}
    short main()
    {
        p[0] = 1;
        fo(i, 1, (1 << 20)) p[i] = p[i - 1] * bas;
        int T = qr;
        while(T--)
        {
            cin >> s; n = s.size(); s = " " + s;
            fo(i, 1, n) has[i] = has[i - 1] * bas + s[i];
            int sum = 0;
            fo(i, 0, 26) b[i] = cnt[i] = 0;
            fo(i, 1, n)
            {
                int zc = s[i] - 'a';
                b[zc] ^= 1;
                sum += b[zc] ? 1 : -1;
                zj[i] = sum;
            }
            sum = 0;
            fill(b, b + 27, 0);
            fu(i, n, 1)
            {
                int zc = s[i] - 'a';
                b[zc] ^= 1;
                sum += b[zc] ? 1 : -1;
                hj[i] = sum;
            }
            ll ans = 0;
            fo(i, 2, n)
            {
                fo(j, zj[i - 1], 26) cnt[j]++;
                for(int j = i; j < n && has[i] == Wgh(j - i + 1, j); j += i) ans += cnt[hj[j + 1]];
            }
            printf("%lld\n", ans);
        }
        return Ratio;
    }
}
signed main(){return Wisadel::main();}
// All talk and never answer

C. 移球游戏

比较有思维含量的一道题。

难得 CCF 思路引导得很好,那就跟着思路过一遍。

首先发现大样例 2 给了一个 \(n=2\) 的情况,跟随手模可以发现正解的大致实现过程:

  • 将柱子二上分出 \(s\) 个到柱子三,\(s\) 为柱子一中 \(1\) 的个数。

  • 将柱子一上 \(1\) 放到柱子二,剩下放到柱子三。此时柱子一为空,柱子二三是满的。

  • 将柱子二上 \(s\)\(1\) 放回柱子一,柱子三上 \(m-s\)\(2\) 放回柱子二。此时柱子一是满的。

  • 将柱子二上全部放到柱子三,将柱子一上 \(m-s\)\(2\) 放到柱子二。此时柱子一上 \(s\)\(1\),柱子二上 \(m-s\)\(2\)

  • 将柱子三上 \(1\) 放到柱子一,\(2\) 放到柱子二。排序完成。

这样操作次数是在 \([4m,5m]\) 之内的,可以接受。不过这只适用于 \(n=2\) 的情况,如何扩展呢?

如果我们通过操作使两种球全部出现在两根柱子上,显然就可以进行上面的操作。如何使两种球出现在两根柱子上?如果我们通过操作使四种球出现在四根柱子上,我们就可以进行至多 \(2\times 2=4\) 次类似的操作就可以实现。那么以此类推,可以发现每次操作相当于处理好后二分成左右两组再次操作。整体上是一种分治的思想。

那么考虑扩展后这个“类似”的操作如何实现。我们只需要将颜色编号为前一半的都放在前一半的柱子上,所以颜色不超过 \(mid\) 与否就对应着 \(n=2\) 中的 \(1/2\)。那么还有一个问题,如果我选的两根柱子上不超过和超过 \(mid\) 的颜色的球数量并不相等怎么办?很简单,一定有一种颜色是超过半数的,我们的操作是一定可以实现一种颜色放置完全的,简单分讨处理多的那一种即可。

分析复杂度,操作总次数递归式为 \(T(n)=2\times T(\frac{n}{2})+ n = n\log n\),每次操作按 \(5m\) 计算,总操作次数为 \(5nm\log n\),时间复杂度为 \(\mathcal{O(n^2m)}\)

点击查看代码
#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 fi first
#define se second
#define pii pair<int, int>
#define P_B(x) push_back(x)
#define M_P(x, y) make_pair(x, y)
const int Ratio = 0;
const int N = 50 + 5, M = 400 + 5;
int n, m;
int st[N][M], top[N], ok[N];
vector<pii> ans;
namespace Wisadel
{
    inline void Wmov(int x, int y)
    {
        ans.P_B(M_P(x, y));
        st[y][++top[y]] = st[x][top[x]--];
    }
    inline void Wsol(int l, int r)
    {
        if(l == r) return ;
        int mid = (l + r) >> 1;
        fo(i, l, r) ok[i] = 0;
        fo(i, 1, mid) fo(j, mid + 1, r)
        {
            if(ok[i] || ok[j]) continue;
            int zci = 0, zcj = 0;
            fo(k, 1, m) zci += (st[i][k] <= mid), zcj += (st[j][k] <= mid);
            if(zci + zcj >= m)
            {
                fo(gg, 1, zci) Wmov(j, n + 1);
                while(top[i]) Wmov(i, st[i][top[i]] <= mid ? j : n + 1);
                fo(gg, 1, zci) Wmov(j, i);
                fo(gg, 1, m - zci) Wmov(n + 1, i);
                while(top[j]) Wmov(j, n + 1);
                fo(gg, 1, m - zci) Wmov(i, j);
                while(top[n + 1]) Wmov(n + 1, (st[n + 1][top[n + 1]] <= mid && top[i] < m) ? i : j);
                ok[i] = 1;
            }
            else
            {
                zci = m - zci, zcj = m - zcj;
                fo(gg, 1, zcj) Wmov(i, n + 1);
                while(top[j]) Wmov(j, st[j][top[j]] > mid ? i : n + 1);
                fo(gg, 1, zcj) Wmov(i, j);
                fo(gg, 1, m - zcj) Wmov(n + 1, j);
                while(top[i]) Wmov(i, n + 1);
                fo(gg, 1, m - zcj) Wmov(j, i);
                while(top[n + 1]) Wmov(n + 1, (st[n + 1][top[n + 1]] > mid && top[j] < m) ? j : i);
                ok[j] = 1;
            }
        }
        Wsol(l, mid), Wsol(mid + 1, r);
    }
    short main()
    {
        n = qr, m = qr;
        fo(i, 1, n)
        {
            top[i] = m;
            fo(j, 1, m) st[i][j] = qr;
        }
        Wsol(1, n);
        printf("%d\n", (int)ans.size());
        for(pii i : ans) printf("%d %d\n", i.fi, i.se);
        return Ratio;
    }
}
signed main(){return Wisadel::main();}
// All talk and never answer
  • 下午

改上午打的 NOIP 2020。

帮助 jijidawang 贺题验题,学到了很多可能很基础但我并不知道的推式子技巧。

还剩四天,但其实完整的就三天了。

想到三天后自己可能就永远离开这熟悉的地方就有些失落,但想得更多的还是正常或超常发挥之类的。光想不做肯定不行,还是得落实到刷的每一道题。三天,对于查漏补缺肯定绰绰有余,认真,平和地对待,迎接属于自己的最好的结果。


未完待续
posted @ 2024-11-25 20:14  Ratio_Y  阅读(65)  评论(4编辑  收藏  举报