Codeforces 707D Persistent Bookcase(时间树)
【题目链接】 http://codeforces.com/problemset/problem/707/D
【题目大意】
给出一个矩阵,要求满足如下操作,单个位置x|=1或者x&=0,一行的数全部取反,回到第k个操作。要求每次操作后输出这个矩阵中数字的和。
【题解】
由于存在操作回溯,考虑使用可持久化数据结构或者建立离线操作树。因为懒,没写持久化。对于每个操作,将它和时间顺序上的上一个节点连边,这样子就形成了一棵树,对这棵树从根节点开始遍历,递归处理,每次处理完一棵子树就回溯操作,这样子就能离线处理操作的持久化了。
【代码】
#include <cstdio> #include <vector> #include <algorithm> using namespace std; const int N=1005,M=100005; int k,q,n,m,t[N],f[N][N],s[N],last=0,ans[M],op[M],x[M],y[M],id[M]; vector<int> v[M]; void dfs(int pre,int w){ int flag=0; if(op[w]==1){ if(f[x[w]][y[w]]^t[x[w]])ans[w]=ans[pre]; else{ f[x[w]][y[w]]^=1; if(f[x[w]][y[w]])s[x[w]]++,flag=1;else s[x[w]]--; ans[w]=ans[pre]+1; } }else if(op[w]==2){ if(f[x[w]][y[w]]^t[x[w]]){ f[x[w]][y[w]]^=1; if(f[x[w]][y[w]])s[x[w]]++,flag=1;else s[x[w]]--; ans[w]=ans[pre]-1; }else ans[w]=ans[pre]; }else if(op[w]==3){ if(t[x[w]]==0)ans[w]=ans[pre]-s[x[w]]+m-s[x[w]]; else ans[w]=ans[pre]-(m-s[x[w]])+s[x[w]]; t[x[w]]^=1; } for(int i=0;i<v[w].size();i++)dfs(w,v[w][i]); if(op[w]==1){ if(ans[w]!=ans[pre]){ f[x[w]][y[w]]^=1; if(flag)s[x[w]]--;else s[x[w]]++; } }else if(op[w]==2){ if(ans[w]!=ans[pre]){ f[x[w]][y[w]]^=1; if(flag)s[x[w]]--;else s[x[w]]++; } }else if(op[w]==3){ t[x[w]]^=1; } } int main(){ scanf("%d%d%d",&n,&m,&q); for(int i=1;i<=q;i++){ scanf("%d",&op[i]); if(op[i]==1){ scanf("%d%d",x+i,y+i); v[last].push_back(id[i]=i); last=i; }else if(op[i]==2){ scanf("%d%d",x+i,y+i); v[last].push_back(id[i]=i); last=i; }else if(op[i]==3){ scanf("%d",x+i); v[last].push_back(id[i]=i); last=i; }else{ scanf("%d",&k); last=id[i]=id[k]; } }dfs(0,0); for(int i=1;i<=q;i++)printf("%d\n",ans[id[i]]); return 0; }
愿你出走半生,归来仍是少年