NOIP2024模拟赛#18 总结

头要炸了。

T1 题面很好懂,手玩了一下发现答案最小是 (m1)×n。可能会多出来一个长度为 k 的部分,会发现如果多出来一个长度为 k 的部分且合法,那么单个串 1k 位与 nk+1n 位一定相同,k+1n 位与 1nk 一定相同。Hash 判一下即可。

写了个模数的 hash,发现过不去样例,改成自然溢出就过了,不懂,赌一把出题人不会卡自然溢出。(伏笔)

T2 怎么又是蛇形方阵,上一次出蛇形方阵头要炸了分数还不高,这一次打算冲一把。

O(n3) 的就是暴力,O(n2) 的应该需要写个函数,要 O(1) 计算 (x,y) 位置的数字是多少。

然后疯狂打表。打表代码:

点击查看代码
for (int T = 1; T <= 20; ++T) {
        n = T;
        int x = 1, y = 1, tot = 0;
        for (int i = n; i > 1; i -= 2) {
            for (int j = 1; j < i; ++j) a[x++][y] = ++tot;
            for (int j = 1; j < i; ++j) a[x][y++] = ++tot;
            for (int j = 1; j < i; ++j) a[x--][y] = ++tot;
            for (int j = 1; j < i; ++j) a[x][y--] = ++tot;
            y ++, x ++;
        }
        if (n & 1) a[x][y] = ++tot;

        x = 1, y = 1;
        LL ans = 0, ans2 = 0;
        while (x <= n && y <= n) {
            if (x <= n / 2 && y <= n / 2)
                ans2 += a[x][y];
            ans += a[x][y];
            ++x, ++y;
        }
        cerr << n << ' ' << ans;
        cout << ' ' << ans2 << ' ' << ans - ans2 << '\n';
    }
    

    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j)
            cerr << setw(4) << a[i][j];
        cerr << '\n';
    }

    for (int i = 1, lastans = 0, lastcha = 0; i <= n; ++i) {
        int x = i, y = 1;
        LL ans = 0;
        while (x <= n && y <= n) {
            ans += a[x][y];
            ++x, ++y;
        }
        cerr << i << ' ' << ans << ' ' << lastans - ans << ' ' << (lastcha - lastans + ans) << '\n';
        lastcha = lastans - ans;
        lastans = ans;
    }
    cerr << '\n';

    for (int i = 1, lastans = 0, lastcha = 0; i <= n; ++i) {
        int x = 1, y = i;
        LL ans = 0;
        while (x <= n && y <= n) {
            ans += a[x][y];
            ++x, ++y;
        }
        cerr << i << ' ' << ans << ' ' << lastans - ans << ' ' << (lastcha - lastans + ans) << '\n';
        lastcha = lastans - ans;
        lastans = ans;
    }

发现对于相邻两行的答案,它们的差值的差值是个等差数列,就是个三阶等差数列。

然后发现只需要算出 k=1 的答案就可以 O(1) 推出其他情况了,这很好。

但不急,先看看后面的题。

T3 感觉树形 DP,但是一时间不会了,写了个 O(2n) 的状压。

T4 想到暴力,但是发现时间复杂度是 O(n3) 的,然后最低档部分分是 n2000,难受。

写了个并查集,样例过不去,分析了一下发现假了。

写了个 vector 维护,最差复杂度 O(n3),但是随机数据下还是挺快的。

然后留了约 1.5 h 冲 T2。

回去看打表规律,发现 k=1 的路径上相邻两项的差是个等差数列。

费了九牛二虎之力写出了 k=1 的做法,O(1)

然后对于其他情况,为保证正确性,先写了个 O(k) 的,但是细节实在太多,头要炸了,比赛结束前 5 min 写出 O(k) 解法,没时间改 O(1) 了,先交了。

预估得分:100+60+20+[0,20]=[180,200]

实际:95+40+20+20=175

T4 数据水了,感谢(。

T1 自然溢出被卡了,据说是应 *** 的要求,赛时加上一组数据卡自然溢出的。赛时取模过不了大样例其实是因为自然溢出和取模混用了。

T2 挂了 20,在于 k=1 的分数,查了下错,除以 2 前面先取模了,唉。

别人 T2 做法代码好短,羡慕,但是还是想实现自己的想法。

下午又用了 1 h 写 T2,头要炸了,终于过了,3.6 kb 喜提第二长解。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
const int P = 998244353;
const int inv6 = 166374059;
LL n, op, k;
LL ANS;
void Add (LL &x, LL y) {
    x = ((x + y % P) % P + P) % P;
}
void solve () {
    cin >> n >> op >> k;
    LL k1 = 0, ans = 0;
    Add (k1, (n + 1) / 2);
    LL x = ((n + n) * 2 - 4 + P) % P, cnt = ((n + 1) / 2 - 1 + P) % P;
    if (n % 2 == 0) Add (k1, 2ll * (cnt + 1) % P * cnt % P);
    Add (k1, 8ll * cnt % P * (cnt + 1) % P * (cnt * 2ll + 1) % P * inv6 % P);
    Add (k1, 1ll * (n + n - 1) % P * (n / 2) % P);
    cnt = (n / 2 - 1 + P) % P; x = ((n + n) * 2 - 8 + P) % P;
    if (n % 2 == 1) Add (k1, 2ll * (cnt + 1) % P * cnt % P);
    Add (k1, 8ll * cnt % P * (cnt + 1) % P * (cnt * 2ll + 1) % P * inv6 % P);
    if (k == 1) {
        ANS ^= k1;
        return ;
    }
    if (op == 1) { // 第 k 行
        if (n & 1) {
            LL cc = -2, nowc = n * n % P;
            ans = (k1 - nowc + P) % P;
            if (k == 2) return ANS ^= ans, void ();
            LL C = (k - 2) * nowc % P;
            LL Cc = (k - 2 + 1) / 2;
        
            Add (ans, -C); Add (ans, -2 * Cc * k % P);
            Add (ans, (1 + Cc) * Cc * 4ll % P * k % P);

            LL Cnt = -1ll * (2 + Cc * 2) * Cc;
            Add (Cnt, 16ll * Cc % P * (Cc + 1) % P * (Cc * 2ll + 1) % P * inv6 % P);
            Add (ans, -Cnt);
            ANS ^= ans;
        }
        else {
            LL cc = 2, nowc = (n * n % P - 2 + P) % P;
            ans = (k1 - nowc + P) % P;
            if (k == 2) return ANS ^= ans, void ();
            LL C = (k - 2) * nowc % P;
            LL Cc = (k - 3 + 1) / 2;
        
            Add (ans, -C); Add (ans, 2 * Cc * k % P);
            Add (ans, (1 + Cc) * Cc % P * 4ll % P * k % P);

            LL Cnt = (10 + 2 + Cc * 8) / 2 * Cc % P;
            Add (Cnt, 1ll * (2 + Cc * 2) % P * Cc);
            Add (Cnt, 16ll * Cc % P * (Cc + 1) % P * (Cc * 2ll + 1) % P * inv6 % P);
            Add (ans, -Cnt);
            ANS ^= ans;
        }
    }
    else { // 第 k 列
        if (n & 1) {
            LL cc = 2, nowc = (n * n - 2 + P) % P;
            ans = (k1 - 1 + P) % P;
            if (k == 2) return ANS ^= ans, void ();
            Add (ans, -nowc);
            if (k == 3) return ANS ^= ans, void ();
            
            LL C = (k - 3) * nowc % P;
            LL Cc = (k - 4 + 1) / 2;
            Add (ans, -C);
            Add (ans, (10 + 10 + (Cc - 1) * 8) / 2 * Cc % P * k % P);

            LL Cnt = 1ll * (4 + 4 + (Cc - 1) * 2 % P) * Cc % P; Cc ++;
            Add (Cnt, 32ll * (Cc % P * Cc % P * Cc % P - Cc + P) % P * inv6 % P);
            Add (ans, -Cnt);
            ANS ^= ans;
        }
        else {
            LL cc = -2, nowc = n * n % P;
            ans = k1;
            if (k == 2) return ANS ^= ans, void ();
            Add (ans, -nowc);
            if (k == 3) return ANS ^= ans, void ();

            k--;
            LL C = (k - 2) * nowc % P;
            LL Cc = (k - 2 + 1) / 2;

            Add (ans, -C); Add (ans, -2 * Cc * k % P);
            Add (ans, (1 + Cc) * Cc % P * 4ll % P * k % P);

            LL Cnt = -1ll * (2 + Cc * 2) * Cc % P;
            Add (Cnt, 16ll * Cc % P * (Cc + 1) % P * (Cc * 2ll + 1) % P * inv6 % P);
            Add (ans, -Cnt);
            ANS ^= ans;
        }
    }
}
signed main () {
    freopen("maze.in", "r", stdin);
    freopen("maze.out", "w", stdout);
    ios::sync_with_stdio (false);
    cin.tie (0); cout.tie (0);
    
    int T; cin >> T;
    while (T--) solve ();
    cout << ANS;
    return 0;
}

T3 倒着做似乎还是比较简单的,启发式合并可以做到 nlog2n,并且跑不满,常数小,可以通过。

T4 正解是神秘转化,然后上树状数组。

当前订正:100+100+100+20

posted @   zhujiangyuan  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示