Codeforces #386 D dfs+bitset

这篇博客介绍了如何解决Codeforces #386 D问题,重点在于利用dfs和bitset进行状态转移。文章阐述了在不能改变时间的状态下,状态转移图形成一维数轴,而在能改变时间时则转变成二维树形结构。通过建立状态转移树,并使用bitset优化状态的更新,从而有效处理书本数量的计算。内容包括相关知识链接、问题解析和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

相关知识


bitset使用 :http://blog.youkuaiyun.com/qll125596718/article/details/6901935


解析


本题的棘手之处在于题目中的第4个操作。在不能改变时间的情况下,状态只能随着时间的推进而转移(一维)。现在可以改变时间,那么状态的转移目的地就不唯一了,也就是说状态的转移会出现分叉。那么状态的转移的图示就从一个数轴变成了一棵树(二维)。如果我们能够构造出这棵树,就能在用 DFS 状态转移的同时计算出每个状态下的书本的数量了。

令询问 i 的时刻为 i ,并对每个时刻设立一个树节点。那么当操作编号为 1,2 或 3 的时候,我们就让 i−1 时刻到 i 时刻连一条单向边,当操作编号为 4 的时候相当于将时刻改变为 k ,那么我们就让 k 时刻到 i 时刻连一条单向边。这样就能构造出我们要的“状态转移树”了。

当然,要在状态转移的过程中计算出书本的数量也并非十分容易的事。关键就在于怎么表示状态。设时刻 i 书本的总数为 res ,我们定义一个 n×m 的矩阵,矩阵的每个元素代表书架的这个位置是否有书。在转移状态的过程中维护这个矩阵就能应付前两个操作(维护 res ),第三个操作要遍历一整行(修改,求和),能否避免这种遍历呢?bitset很好的节省了时间。


代码

#include<cstdio>
#include<cstring>
#include <iostream>
#include <queue>
#include <vector>
#include <bitset>
using namespace std;
const int maxn = 1000+10;
const int inf = 0x3f3f3f3f;
typedef pair<int, int> P;
int n, m, q;
struct node {
    int com, i, j;
}op[100000+10];
vector<int>g[100000+10];
int ans[100000+10];
int vis[100000+10];
bitset<maxn>bit[maxn];
int X[maxn];
int res;
void dfs(int u) {
    ans[u] = res;
    for (int i=0; i<g[u].size(); i++) {
        int v = g[u][i];
        int pre = res;
        bitset<1010>b;
        int tmp = X[op[v].i];
        b = bit[op[v].i];
        if (op[v].com == 1) {
            int x = op[v].i, y = op[v].j;
            if (!bit[x].test(y)) {
                res++;
                bit[x].set(y);
            }
        }
        else if (op[v].com == 2) {
            int x = op[v].i, y=op[v].j;
            if (bit[x].test(y)) {
                res--;
                bit[x].reset(y);
            }
        }
        else if (op[v].com == 3) {
            int x=op[v].i;
            if (X[x] % 2 == 0) {
                int num = b.count();
                X[x]++;
                int ren = m-num;
                res += (ren-num);
                bit[x].flip();
            }
            else {
                int num = bit[x].count()-1010+m;
                X[x]++;
                res += (m-2*num);
                bit[x].flip();
            }
        }
        dfs(v);
        res = pre;
        bit[op[v].i] = b;
        X[op[v].i] = tmp;
    }
}

int main() {
    res = 0;
    scanf("%d%d%d", &n, &m, &q);
    for (int i=0; i<=q; i++)
        g[i].clear();
    for (int i=1; i<=q; i++) {
        int com;
        scanf("%d", &com);
        if (com <= 2) {
            op[i].com = com;
            scanf("%d%d", &op[i].i, &op[i].j);
            g[i-1].push_back(i);
        }
        else if (com == 3) {
            op[i].com = com;
            scanf("%d", &op[i].i);
            g[i-1].push_back(i);
        }
        else if (com == 4) {
            int k;
            scanf("%d", &k);
            g[k].push_back(i);
        }
    }
    dfs(0);
    for (int i=1; i<=q; i++)
        printf("%d\n", ans[i]);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值