给定一个n*m的矩阵,每个元素都是0或1,一共4种操作,操作q次。
1、把(i,j)改成1
2、把(i,j)改成0
3、翻转第i行
4.回溯变成第k次操作时的矩阵
每次操作完后输出矩阵所有元素之和。
(1<=n,m<=1000,q<=1e5)
涉及回溯历史版本,可持久化线段树。
我们把每行看成一个整体,单独一行涉及的操作是单点修改,区间反转,查询区间和,如果我们对每行都建线段树,即:用线段树维护n棵线段树之和,那么肯定会爆空间的。
单点修改,区间反转,查询区间和
我们可以用一个空间位1000的bitset来实现。
理论上空间和时间都会缩小64倍。
即:
每个叶子节点开一个1000位的bitset,维护区间和,即:1的个数。
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 1e5 + 10;
const ll mod = 1e9 + 7;
const ll inf = (ll)4e16+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){while(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){while(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct node
{
bitset<1005> col;
int sum;
}tree[maxn*20];
int lc[maxn*20],rc[maxn*20];//nlogq的空间
int root[maxn],tot;
int n,m,q;
inline void pushup(int rt)
{
tree[rt].sum=tree[lc[rt]].sum + tree[rc[rt]].sum;
}
inline void upd(int &rt,int last,int l,int r,int pos,int f,int j)
{
rt=++tot;
lc[rt]=lc[last],rc[rt]=rc[last];
tree[rt]=tree[last];
if(l==r)
{
if(f==1)
{
if(!tree[rt].col[j]) tree[rt].sum++;
tree[rt].col.set(j,1);
}
else if(f==2)
{
if(tree[rt].col[j]) tree[rt].sum--;
tree[rt].col.set(j,0);
}
else
{
tree[rt].col.flip();
tree[rt].sum=m-tree[rt].sum;
}
return ;
}
int mid=l+r>>1;
if(pos<=mid) upd(lc[rt],lc[last],l,mid,pos,f,j);
else upd(rc[rt],rc[last],mid+1,r,pos,f,j);
pushup(rt);
}
int main()
{
scanf("%d %d %d",&n,&m,&q);
for(int i=1,f,i1,j,k;i<=q;i++)
{
scanf("%d",&f);
if(f==1 || f==2 )
{
scanf("%d %d",&i1,&j);
upd(root[i],root[i-1],1,n,i1,f,j);
}
else if(f==3)
{
scanf("%d",&i1);
upd(root[i],root[i-1],1,n,i1,f,0);
}
else
{
scanf("%d",&k);
root[i]=root[k];
}
printf("%d\n",tree[root[i]].sum);
}
return 0;
}