Codeforces 707D Persistent Bookcase(操作建树)

20 篇文章 0 订阅
11 篇文章 0 订阅
D. Persistent Bookcase
time limit per test
2 seconds
memory limit per test
512 megabytes
input
standard input
output
standard output

Recently in school Alina has learned what are the persistent data structures: they are data structures that always preserves the previous version of itself and access to it when it is modified.

After reaching home Alina decided to invent her own persistent data structure. Inventing didn't take long: there is a bookcase right behind her bed. Alina thinks that the bookcase is a good choice for a persistent data structure. Initially the bookcase is empty, thus there is no book at any position at any shelf.

The bookcase consists of n shelves, and each shelf has exactlym positions for books at it. Alina enumerates shelves by integers from1 to n and positions at shelves — from1 to m. Initially the bookcase is empty, thus there is no book at any position at any shelf in it.

Alina wrote down q operations, which will be consecutively applied to the bookcase. Each of the operations has one of four types:

  • 1 i j — Place a book at position j at shelfi if there is no book at it.
  • 2 i j — Remove the book from position j at shelfi if there is a book at it.
  • 3 i — Invert book placing at shelfi. This means that from every position at shelfi which has a book at it, the book should be removed, and at every position at shelfi which has not book at it, a book should be placed.
  • 4 k — Return the books in the bookcase in a state they were after applyingk-th operation. In particular, k = 0 means that the bookcase should be in initial state, thus every book in the bookcase should be removed from its position.

After applying each of operation Alina is interested in the number of books in the bookcase. Alina got 'A' in the school and had no problem finding this values. Will you do so?

Input

The first line of the input contains three integers n,m and q (1 ≤ n, m ≤ 103,1 ≤ q ≤ 105) — the bookcase dimensions and the number of operations respectively.

The next q lines describes operations in chronological order —i-th of them describes i-th operation in one of the four formats described in the statement.

It is guaranteed that shelf indices and position indices are correct, and in each of fourth-type operation the numberk corresponds to some operation before it or equals to0.

Output

For each operation, print the number of books in the bookcase after applying it in a separate line. The answers should be printed in chronological order.


Examples
Input
2 3 3
1 1 1
3 2
4 0
Output
1
4
0
Input
4 2 6
3 2
2 2 2
3 3
3 2
2 2 2
3 2
Output
2
1
3
3
2
4
Input
2 2 2
3 2
2 2 1
Output
2
1
Note

This image illustrates the second sample case.

题目大意:

    有一个开始是空的书柜,有四种操作,在某一位置加一本书,在某一位置减一本书,将某一行的书的状态反转(有书就取下,没书就加上)。回到某一次操作的状态。输出每一次操作后书架上书的个数。


解题思路:

    题目一开始就说可持续化数据结构,再加上这题的输入输出又是这么标准的数据结构题模式,我就没有向其它方向思考。其实这题完全不用什么可持续化数据结构!!!只要离线处理,对每个操作按照顺序建一棵树。然后dfs模拟一下就可以了。。。

    具体的建树:如果是操作4,我们把这个操作接在输入的k后面,否则接到上一个操作后面。(详情见代码)


AC代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;

const int maxn=1000+3;
const int maxq=100000+3;
int N,M,Q,op[maxq],x[maxq],y[maxq];
vector<int> G[maxq];
bool shell[maxn][maxn];
int ans[maxq],num;

void dfs(int u)
{
    int done=0;//执行的操作,回溯用
    if(op[u]==1&&!shell[x[u]][y[u]])
    {
        done=1;
        ++num;
        shell[x[u]][y[u]]=true;
    }
    else if(op[u]==2&&shell[x[u]][y[u]])
    {
        done=2;
        --num;
        shell[x[u]][y[u]]=false;
    }
    else if(op[u]==3)
    {
        for(int i=1;i<=M;++i)
        {
            if(shell[x[u]][i])
            {
                --num;
                shell[x[u]][i]=false;
            }
            else
            {
                ++num;
                shell[x[u]][i]=true;
            }
        }
    }
    ans[u]=num;//记录结果
    for(int i=0;i<G[u].size();++i)
        dfs(G[u][i]);
    if(op[u]==1&&done==1)//回溯
    {
        --num;
        shell[x[u]][y[u]]=false;
    }
    else if(op[u]==2&&done==2)
    {
        ++num;
        shell[x[u]][y[u]]=true;
    }
    else if(op[u]==3)
    {
        for(int i=1;i<=M;++i)
        {
            if(shell[x[u]][i])
            {
                --num;
                shell[x[u]][i]=false;
            }
            else
            {
                ++num;
                shell[x[u]][i]=true;
            }
        }
    }
}

int main()
{
    scanf("%d%d%d",&N,&M,&Q);
    for(int i=1;i<=Q;++i)
    {
        scanf("%d%d",&op[i],&x[i]);
        if(op[i]==1||op[i]==2)
            scanf("%d",&y[i]);
        if(op[i]==4)//建树
            G[x[i]].push_back(i);
        else G[i-1].push_back(i);
    }
    for(int i=0;i<G[0].size();++i)
        dfs(G[0][i]);
    for(int i=1;i<=Q;++i)
        printf("%d\n",ans[i]);
    
    return 0;
}


CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值