对一个 n×m 的零矩阵 A 进行 q 次操作:
∙1 i j :将 Ai,j 取反 (xor 1)
∙2 i :将矩阵 A 第 i 行所有元素全部取反
∙3 j :将矩阵 A 第 j 列所有元素全部取反
∙4 k :将矩阵 A 还原为第 k 次操作之后的状态。
进行每一次操作之后,询问当前矩阵所有元素的和。
1≤n,m≤103,q≤105
这题乍一看要支持完全可持久化,很难做。
观察发现其实这些操作之前形成了树形关系,一个操作一定是某一个操作的后继。其实这个东西就是操作树。
我们构造出操作树之后,就可以在树上便利一遍。每次经过一条边(包括退出时,因为同一个操作执行两次就是撤销)就执行这条边上的操作并记录答案。你问我怎么执行?暴力啊~
还有就是注意可能会回到第 0 个操作。
时间复杂度 O(nq) 。
#include
#include
using namespace std;
const int Q=100005;
const int N=1005;
int fa[Q],last[Q],tov[Q],next[Q],opt[Q][3],ans[Q];
int n,m,q,sum,tot;
bool mat[N][N];
inline void insert(int x,int y){tov[++tot]=y,next[tot]=last[x],last[x]=tot;}
void dfs(int x)
{
if (x)
{
switch (opt[x][0])
{
case 1:
{
sum-=mat[opt[x][1]][opt[x][2]];
mat[opt[x][1]][opt[x][2]]^=1;
sum+=mat[opt[x][1]][opt[x][2]];
break;
}
case 2:
{
for (int c=1;c<=m;c++) sum-=mat[opt[x][1]][c],mat[opt[x][1]][c]^=1,sum+=mat[opt[x][1]][c];
break;
}
case 3:
{
for (int r=1;r<=n;r++) sum-=mat[r][opt[x][1]],mat[r][opt[x][1]]^=1,sum+=mat[r][opt[x][1]];
break;
}
}
ans[x]=sum;
}
for (int i=last[x];i;i=next[i]) dfs(tov[i]);
if (x)
{
switch (opt[x][0])
{
case 1:
{
sum-=mat[opt[x][1]][opt[x][2]];
mat[opt[x][1]][opt[x][2]]^=1;
sum+=mat[opt[x][1]][opt[x][2]];
break;
}
case 2:
{
for (int c=1;c<=m;c++) sum-=mat[opt[x][1]][c],mat[opt[x][1]][c]^=1,sum+=mat[opt[x][1]][c];
break;
}
case 3:
{
for (int r=1;r<=n;r++) sum-=mat[r][opt[x][1]],mat[r][opt[x][1]]^=1,sum+=mat[r][opt[x][1]];
break;
}
}
}
}
void solve()
{
for (int i=1,op,x,y;i<=q;i++)
{
scanf("%d%d",&op,&x);
switch (op)
{
case 1:
{
scanf("%d",&y);
fa[i]=i-1,insert(i-1,i),opt[i][0]=1,opt[i][1]=x,opt[i][2]=y;
break;
}
case 2:
{
fa[i]=i-1,insert(i-1,i),opt[i][0]=2,opt[i][1]=x;
break;
}
case 3:
{
fa[i]=i-1,insert(i-1,i),opt[i][0]=3,opt[i][1]=x;
break;
}
case 4:
{
fa[i]=x,insert(x,i);
break;
}
}
}
dfs(0);
for (int i=1;i<=q;i++) printf("%d\n",ans[i]);
}
int main()
{
freopen("present.in","r",stdin),freopen("present.out","w",stdout);
scanf("%d%d%d",&n,&m,&q);
solve();
fclose(stdin),fclose(stdout);
return 0;
}