题意:
对一个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;
}