【题解】CF707D Persistent Bookcase
\(\text{Solution:}\)
先考虑前三个操作,用\(rev[i]\)表示第\(i\)行是不是被反转,\(vis[i][j]\)表示点\((i,j)\)是不是被反转。
若\(vis[i][j]\not=rev[i],\)意味着\(a[i][j]=0.\)反之若\(vis[i][j]=rev[i],\)意味着\(a[i][j]=1.\)
解释:若\(vis[i][j]\not=rev[i],\)则说明这一行反转了而这个点没有再次反转,或者是这一行没有反转过而这一个点反转了,两者都表示\(a[i][j]=1.\)
后面同理。
考虑如何维护操作\(4:\)
考虑建立操作树,将操作之间连边,并将四操作之间的点连边。这意味这我们可以在执行到\(k\)操作的时候先处理有操作\(4\to k\)后面的操作。也就规避了所谓要持久化的要求。
同时观察到,若\(x\to y,\)则\(\forall i\in [x+1,y-1],i\not\to y.\)因为一个点最多引出一条边。
于是复杂度和正确性得以保证。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=3e5+10;
bool vis[10005][10005],rev[10005];
struct E{int nxt,to;}e[MAXN];
int tot,head[MAXN];
inline void add(int x,int y){e[++tot]=(E){head[x],y};head[x]=tot;}
int opt[MAXN],X[MAXN],Y[MAXN],s[MAXN],m,n,q;
typedef long long ll;
ll ans[MAXN];
void dfs(int x,int sum){
int tmp=0,i=x;
if(opt[i]==1){if(vis[X[i]][Y[i]]==rev[X[i]])tmp=1,++sum,++s[X[i]],vis[X[i]][Y[i]]=!rev[X[i]];}
if(opt[i]==2){if(vis[X[i]][Y[i]]!=rev[X[i]])tmp=1,--sum,--s[X[i]],vis[X[i]][Y[i]]=rev[X[i]];}
if(opt[i]==3){sum+=m-s[X[i]]-s[X[i]];rev[X[i]]^=1;s[X[i]]=m-s[X[i]];}
ans[i]=sum;for(int i=head[x];i;i=e[i].nxt)dfs(e[i].to,sum);
if(opt[x]==1&&tmp)--sum,--s[X[i]],vis[X[i]][Y[i]]=rev[X[i]];
if(opt[x]==2&&tmp)++sum,++s[X[i]],vis[X[i]][Y[i]]=!rev[X[i]];
if(opt[x]==3)sum-=m-s[X[i]]-s[X[i]],rev[X[i]]^=1,s[X[i]]=m-s[X[i]];
}
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=q;++i){
scanf("%d%d",&opt[i],&X[i]);
if(opt[i]<3)scanf("%d",&Y[i]);
if(opt[i]==4)add(X[i],i);
else add(i-1,i);
}
dfs(0,0);
for(int i=1;i<=q;++i)printf("%lld\n",ans[i]);
return 0;
}