Loading

2024杭电多校第6场

6

1004 不醒人室 (hdu7497

模拟题,思路和题解略有不同,由于数据保证各时间段有序,可在循环外设变量 \(awake\) 表示在当前循环位置、最后清醒的时间,遍历上课时间的同时用计划的睡觉时间更新 \(awake\) 即可,时间复杂度为线性。

主要代码:

    ll awake = 0;
    int j = 1;
    for(int i = 1; i <= n; i++) {
        ll l = c[i].l, r = c[i].r; //c[i]课程时间,s[i]睡觉时间
        if(awake < r) {
            if(awake >= l) awake = -1;
            for(; j <= m; j++) {
                ll st = s[j].r, ed = s[j].r + (s[j].r - s[j].l) * 2;
                if(j < m) ed = min(ed, s[j + 1].l);
                if(st <= l && ed >= r) {
                    awake = ed;
                    break;
                }
                else if(st > l) {
                    awake = -1;
                    break;
                }
            }
            if(awake < 0) break;
        }
    }

至于为什么思路讲得有点抽象,因为本来就没啥思路······这场杭电的前一天晚上打cf去了(参见上一篇题解),赛时脑子还不太清醒,一通乱写过了wyq的数据就交,没想到对了······ wyq评价:最符合题意的一集)

1005 交通管控 (hdu7498

\(k\) 的数据范围想到状压dp,红绿灯三种颜色状态需要三进制状压。对于相同的操作杆组合,每根操作杆的使用顺序不影响最终结果,因此用类似背包的方式求解,使用滚动数组可优化空间复杂度至 \(3^{10}\) 左右。答案对 \(m\) 取模,由于最终答案可能是 \(m\) 的整数倍,取模后为 \(0\),无法区分是否出现该组合,需要另开一个bool数组表示该状态的合法性。

以下为dp代码,需注意初始化:

    for(int s = 0; s < p[k]; s++) {
        dp[s][0] = dp[s][1] = 0;
        vis[s][0] = vis[s][1] = 0;
    }
    dp[0][0] = 1;
    vis[0][0] = 1;
    for(int i = 1; i <= n; i++) {
        int ii = i & 1;
        for(int s = 0; s < p[k]; s++) {
            dp[s][ii] = 0;
            vis[s][ii] = 0;
        }
        string c;
        cin >> c;
        for(int s = 0; s < p[k]; s++) {
            if(!vis[s][ii ^ 1]) continue;
            vis[s][ii] = 1;
            dp[s][ii] += dp[s][ii ^ 1];
            dp[s][ii] %= mo;
            int to = 0;
            for(int z = 0; z < k; z++) {
                int cur = (s / p[z]) % 3;
                if(c[z] == '+') cur = (cur + 1) % 3;
                else if(c[z] == '-') cur = (cur + 2) % 3;
                to += p[z] * cur;
            }
            vis[to][ii] = 1;
            dp[to][ii] += dp[s][ii ^ 1];
            dp[to][ii] %= mo;
        }
    }

除此以外我还尝试过分层图+拓扑的方法,逻辑严不严谨另说,由于空间无法优化,喜提MLE

1007 树上 \(MEX\) 问题 (hdu7500

赛时没想到如何统计最终答案,看了眼题解似乎能动态维护,于是按照之前的想法继续写,还真可以)

直接统计子图的 \(MEX\) 值非常麻烦,可从每个子图对答案的“贡献”角度思考。若某一子图中包含权值为 \(0\)\(x - 1\) 的所有点,可确定其 \(MEX\geq x\),即该子图的贡献不小于 \(x\). 先假设所有子图 \(MEX = 0\),对于包含 \(0\) 权值的 \(t\) 个子图,其贡献至少为 \(1\),故应补充答案,有 \(sum\) \(+\)\(=\) \(t\);在此基础上,对于同时包含 \(0,1\) 权值的子图,同理有 \(sum\) \(+\)\(=\) \(t'\);······ 以此类推,若每种子图的数量可快速计算,即可 \(O(n)\) 统计答案。

树形dp可 \(O(n)\) 求出以每个节点为根节点的子图数量,递推即乘法原理 \(cnt[i] = \prod (cnt[t] + 1)\). 对于包含 \(0\)\(x - 1\) 所有节点的子图数量 \(ans\),考虑动态维护,每次加入新节点时计算 \(ans\) 的变化量即可。以 \(0\) 权值点为根,前 \(x - 1\) 个节点形成的最小子图为 \(s\),若 \(x\) 权值在 \(s\) 内部,所有包含 \(s\) 的子图必然包含 \(x\)\(ans\) 不变;若 \(x\)\(s\) 外,由于 \(s\) 内部所有点必然与根节点 \(0\) 权值处连接,\(x\) 不可能是它们的祖先,因此顺着 \(x\) 的父亲节点向上寻找,即可将 \(x\) 加入 \(s\) 中。如此处理后,\(x\) 至其祖先的链上答案发生变化,其他位置仍然遵循乘法原理,设该链上子图总数 \(k\),答案变化 \(ans = (ans/k) * k'\)\(x\) 向上寻找父节点 \(f\),有 \(k'=k'*cnt[f]/(cnt[x] + 1)\),原先数量除去所有不选择 \(x\) 的情况即可。由于每个点只被加入 \(s\) 一次,复杂度仍然为 \(O(n)\).

    sum = cnt[mp[0]];
    in[mp[0]] = 1; //in数组表示点是否在s内,由于每次操作的s固定,不需要用并查集
    ll ans = cnt[mp[0]];
    for(int i = 1; i < n; i++) {
        int x = mp[i];
        if(in[x] == 0) {
            in[x] = 1;
            ll tim = cnt[x]; //即原文中k'
            int pre = x;
            for(int j = f[x]; ; j = f[j]) {
                if(in[j]) {
                    ans = ans * tim % mo * inv(cnt[pre] + 1) % mo; //cnt[pre] + 1即原文中k
                    sum += ans;
                    if(sum >= mo) sum -= mo;
                    break;
                }
                tim = tim * cnt[j] % mo * inv(cnt[pre] + 1) % mo;
                in[j] = 1;
                pre = j;
            }
        } else {
            sum += ans;
            if(sum >= mo) sum -= mo;
        }
    }
    printf("%lld\n", sum);
posted @ 2024-08-07 16:16  Aderose_yr  阅读(60)  评论(0编辑  收藏  举报