codeforces 707 D Persistent Bookcase(dfs+bitset)

106 篇文章 0 订阅
64 篇文章 0 订阅

题意:

对一个n*m的01矩阵进行操作。

第一种操作是将x行y列的数置1.

第二种操作是将x行y列的数置0.

第三种操作是将x行的数取反。

第四种操作是将矩阵回到第x次操作后的矩阵。

每次操作后都询问当前矩阵有多少个1。


解题思路:

说是可持久化数据结构,我最近又刚刚学了主席树,就真的被骗去写可持久化了,然后还没有写出来QAQ。


其实这个题目由于n,m小于1000,本身操作消耗的时间很短,所以不需要可持久化。

但是如果暴力恢复到之前的版本,也是不行的。

我们需要对恢复操作做一些优化,至于前三种操作嘛,当然是直接模拟咯。


操作数量是100000,试想一下如果有后一半的操作全部是回退到刚开始的版本,那岂不是直接n^2的复杂度了,这中间也产生了很多的重复回退的计算。比如第9次回退到第0次的版本,然后第十次需要回退到第8次操作,又要进行一次重复的回退运算。所以我们只要将这种重复的运算给消掉,想办法让复杂度降到o(操作数量)就可做了。


我们把操作当成一个节点,建一棵树,对于前三种操作,父亲就是前一次操作,对于第四种回退操作,父亲就是要回退的第x次操作。


这样的话,每次dfs的时候都把操作执行一次,回溯的时候再把对应操作逆执行一次,那么dfs到相应操作时候,就正好得到了当前状态下的矩阵了。然后对于之前的例子,也是少了一次回退,再dfs到第8次操作后直接dfs第10次操作,然后回溯的到第九次操作。


整个过程容易想到是o(操作数量)的。


代码:

#include <bits/stdc++.h>
#define ps push_back
using namespace std;
const int maxq=1e5+5;
const int maxn=1e3+5;
int n, m, q, ans;
bitset<maxn>rec[maxn];
struct node
{
    int tp, x, y;
}op[maxq];
int res[maxq];
vector<int>edg[maxq];

void dfs(int id)
{
//    printf("%d\n", id);
    int x, y, tp, tmp;
    x=op[id].x, y=op[id].y, tp=op[id].tp;

    if(id>0)
    if(tp==1)
    {
        tmp=rec[x][y];
        ans+=(rec[x][y]==0);
        rec[x][y]=1;
    }
    else if(tp==2)
    {
        tmp=rec[x][y];
        ans-=(rec[x][y]==1);
        rec[x][y]=0;
    }
    else if(tp==3)
    {

        for(int i=1; i<=m; i++)
        {
            ans=rec[x][i]?ans-1:ans+1;
            rec[x][i]=!rec[x][i];
        }
    }

    res[id]=ans;
    for(int i=0; i<(int)(edg[id].size()); i++)
    {
        dfs(edg[id][i]);
    }
    if(id>0)
    if(tp==2)
    {
        ans+=(tmp-rec[x][y]);
        rec[x][y]=tmp;
    }
    else if(tp==1)
    {
        ans+=(tmp-rec[x][y]);
        rec[x][y]=tmp;
    }
    else if(tp==3)
    {
        for(int i=1; i<=m; i++)
        {
            ans=rec[x][i]?ans-1:ans+1;
            rec[x][i]=!rec[x][i];
        }
    }
    return;
}
int main()
{
    cin>>n>>m>>q;
    int i, j;
    for(i=1; i<=q; i++)
    {
        scanf("%d", &op[i].tp);
        if(op[i].tp==1 || op[i].tp==2)
        {
            scanf("%d%d", &op[i].x, &op[i].y);
            edg[i-1].ps(i);
        }
        else if(op[i].tp==3)
        {
            scanf("%d", &op[i].x);
            edg[i-1].ps(i);
        }
        else
        {
            scanf("%d", &op[i].x);
            edg[op[i].x].ps(i);
        }
    }

    dfs(0);

    for(i=1; i<=q; i++)printf("%d\n", res[i]);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值