#2488. 象棋游戏(topovi)
题目描述
Mirko是一个象棋爱好者,他在一个 $N \times N$ 的棋盘上放置 $K$ 个车,游戏规则如下:
- 每个车有一个权值。
- 每个车可以看到它所在的行和列上的所有其他格子(不包括本身)。
- 一个格子被攻击当且仅当它所能看到的所有车的权值异或值大于 $0$ 。
现在,Mirko要对棋盘上的一些车进行 $P$ 次移动,每个车可以移动到棋盘上的任意一个空格子(不只是行和列上的移动),请你帮忙计算对于每次移动后棋盘上有多少个格子被攻击
数据范围
对于100%数据,$1 \le N \le 1000000000, 1 \le K \le 100000, 1 \le P \le 100000, 1 \le R,C \le N, 1 \le X \le 1000000000$
题解
看对题比较重要
可以转化一下题意:找出有多少数对 $(r,c)$ ,其中第 $r$ 行的异或和和第 $c$ 列的异或和不等
考虑到求相等更方便,最后用 $n \times n-cnt$ 即可
于是用 $map_{r/c,v}$ 表示的是异或和是 $v$ 的行数/列数,注意异或和等于 $0$ 的要加上未出现过的行数/列数
修改的时候考虑到不在原来或新的位置所在的行或列的格子的贡献是不受影响的,于是只要重新计算下原来或新的位置的行或列上的格子贡献即可
效率: $O((k+p)logn)$
代码
#include <bits/stdc++.h> #define M make_pair #define LL long long using namespace std; const int N=2e5+5; int n,k,p,vr[N],vc[N],dr,dc; map<int,int>R,C,Vr,Vc; map<pair<int,int>,int>g;LL ans; int main(){ scanf("%d%d%d",&n,&k,&p); for (int r,c,x,kr,kc,i=1;i<=k;i++){ scanf("%d%d%d",&r,&c,&x);g[M(r,c)]=x; kr=R[r];if (!kr) kr=R[r]=++dr;vr[kr]^=x; kc=C[c];if (!kc) kc=C[c]=++dc;vc[kc]^=x; } Vc[0]+=n-dc; for (int i=1;i<=dr;i++) Vr[vr[i]]++; for (int i=1;i<=dc;i++) Vc[vc[i]]++; for (int i=1;i<=dr;i++) ans+=Vc[vr[i]]; ans+=1ll*(n-dr)*Vc[0];Vr[0]+=n-dr; for (int v,r1,c1,r2,c2;p--;){ scanf("%d%d%d%d",&r1,&c1,&r2,&c2); if (!R[r2]) R[r2]=++dr; if (!C[c2]) C[c2]=++dc; v=g[M(r1,c1)];g[M(r2,c2)]=v; r1=R[r1];r2=R[r2];c1=C[c1];c2=C[c2]; ans-=Vr[vc[c1]]+(c1!=c2)*Vr[vc[c2]]; ans-=Vc[vr[r1]]+(r1!=r2)*Vc[vr[r2]]; if (vr[r1]==vc[c1]) ans++; if (vr[r2]==vc[c2]) ans++; if (r1!=r2 && c1!=c2){ if (vr[r1]==vc[c2]) ans++; if (vr[r2]==vc[c1]) ans++; } Vr[vr[r1]]--;if (r1!=r2) Vr[vr[r2]]--; Vc[vc[c1]]--;if (c1!=c2) Vc[vc[c2]]--; vr[r1]^=v;vr[r2]^=v;vc[c1]^=v;vc[c2]^=v; Vr[vr[r1]]++;if (r1!=r2) Vr[vr[r2]]++; Vc[vc[c1]]++;if (c1!=c2) Vc[vc[c2]]++; ans+=Vr[vc[c1]]+(c1!=c2)*Vr[vc[c2]]; ans+=Vc[vr[r1]]+(r1!=r2)*Vc[vr[r2]]; if (vr[r1]==vc[c1]) ans--; if (vr[r2]==vc[c2]) ans--; if (r1!=r2 && c1!=c2){ if (vr[r1]==vc[c2]) ans--; if (vr[r2]==vc[c1]) ans--; } printf("%lld\n",1ll*n*n-ans); } return 0; }