TOPOVI
建议管理大大把这道题评蓝。理由:运用了异或的性质。
首先考虑每个棋子对哪些格子造成影响。显然对于所有与它同行同列且不和它重合的格子,加入这个棋子会使得这些格子的权值异或上这个棋子的权值。但题目中说明了棋子不会对自己的格子造成任何影响,而我们知道任何数异或自己等于零,所以这个没有影响可以看成是当前格子的权值异或了两次棋子的权值。那么就可以总结出放置棋子的影响:该棋子先使得所在行的所有元素异或它的权值,然后再使所在列的所有元素异或它的权值。只需要获知每一列和每一列对应的值,异或起来就是对应格子的权值了。
然后考虑每一行对答案的影响。假如有一行,这一行上所有元素都异或了 \(a_i\) ,那么这一行的元素 \(A_{i,j}\) 非零当且仅当 \(a_i\oplus b_j\ne 0\) ,而一行总共有 \(size\) 个元素,那么这一行非零的元素的数量可以表示为 \(num=size-\sum\limits_j^{size}[a_i\oplus b_j=0]\) ,即是矩阵大小减去恰好等于 \(a_i\) 的 \(b_j\) 的个数。统计某个值出现了多少次可以使用 map 来做。
整理一下这道题的流程。每放置一个棋子就给对应的行和列异或上它的权值,然后统计这次放置对答案的影响。新的答案应该是旧的答案减去放置之前这一行和列上非零元素的个数再加上放置之后非零元素的个数(因为其它地方不会受到这个棋子的影响,所以统计这行和这列的答案即可)。至于移动棋子,可以看成是添加两个棋子(对于原位置来说,把它赋值为 \(0\) 等价于再异或上一次它本身),重复上面的操作即可。至于统计答案可以直接用上一段所说的方法来快速统计。数据范围很大,要用 map 来存储各种信息,所以复杂度为 \(O(N\log N)\) 。
代码有注释,大佬轻喷。
#include<bits/stdc++.h>
//#define feyn
#define ll long long
using namespace std;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w>='0'&&w<='9'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
struct node{int x,y;};
inline bool operator <(node s1,node s2){
return s1.x==s2.x?s1.y<s2.y:s1.x<s2.x;
}
map<node,int>val;
int size,m,n;
map<int,int>a,b;//每行、每列具体的值
map<int,int>ta,tb;//每个值对应的数量
ll ans;
inline void work(int s1,int s2,int s3){//给s1行和s2列的所有元素异或s3并更新答案
int aa=a[s1],ab=b[s2];
ans-=size-tb[aa];ans-=size-ta[ab];//去掉这一行和这一列所有的非零元素
if(aa^ab)ans++;//考虑这个格子是否重复计算
ta[aa]--;tb[ab]--;//原来的值就少了一个
aa^=s3;ab^=s3;ta[aa]++;tb[ab]++;//现在的值多了一个
ans+=size-tb[aa];ans+=size-ta[ab];//加上这一行和这一列的非零元素
if(aa^ab)ans--;
a[s1]=aa;b[s2]=ab;//更新行和列的值
}
signed main(){
#ifdef feyn
freopen("in.txt","r",stdin);
#endif
read(size);read(m);read(n);
int s1,s2,s3;
ta[0]=tb[0]=size;
for(int i=1;i<=m;i++){
read(s1);read(s2);read(s3);
val[(node){s1,s2}]=s3;//更新这个位置的值
work(s1,s2,s3);//给这个位置加上一个棋子
}
while(n--){
read(s1);read(s2);
int data=val[(node){s1,s2}];
work(s1,s2,data);
read(s1);read(s2);
work(s1,s2,data);//移动相当于是添加了两个棋子
val[(node){s1,s2}]=data;//更新当前位置的值
printf("%lld\n",ans);
}
return 0;
}