【题解】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;
} 
posted @ 2020-10-25 20:11  Refined_heart  阅读(151)  评论(0编辑  收藏  举报