P6532 [COCI2015-2016#1] TOPOVI 题解
题目分析
又是考试遇到的,考场完全没思路,只写了暴力。
这道题其实不难,没有那么难想,但也不是那么的不好想。
转化以下题意,其实就是每行的异或和与每列的异或和有多少对相等(因为冲突即不等,就是总的减去等的)。
再想,其实最终答案只与值和行还是列有关,与哪些地方是这些值无关,所以可以用类似桶的东西来存这个值有多少个。
又由于这个值会很大,可以用 map 来存:lv[i]
表示值为 i
的行数是多少,列同理。
现在求值很方便了,但是还有修改啊。(这里讲的是把一个格子所在行列异或一个值)
自然而然就可以想到同样用 map 来存每行和每列当前的异或和,修改时将之前的值对应个数减一,新值加一,同时修改对应行列的值。
那答案的修改呢?
以行为例:当前值的贡献是与其值相同的列的个数(就是之前统计的那个)。这样就可以减去之前值得贡献再加上新的值得贡献来得到答案了。
还有一个小细节:在计算贡献时,如果这个点的行列值相同,会算两次。可以特判解决,但没必要。异或之前相等,那异或之后也相等,减的时候多减了一次,那加的时候也会多加一次就抵消了。
还有一个问题就是这个格子的值不会修改,但直接整行整列异或不会出问题吗?——不会,因为最后判断这个格子是否有冲突是行异或列,而行和列都异或过当前格子的值,异或两次就等于没有异或。
Code
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define map unordered_map
const int N=2e5+5;
map<int,int> l,r;
map<int,int> lv,rv;
map<int,int> mp;
int ans;
inline int read(){
int sum=0,f=1;char a=getchar();
while(a<'0' || a>'9'){if(a=='-') f=-1;a=getchar();}
while(a>='0' && a<='9') sum=sum*10+a-'0',a=getchar();
return sum*f;
}
void change(int x,int y,int z){//修改这个点的值
// if(l[x]==r[y]) ++ans;//两个特判,都可以不用(当然不能只用一个啊)
ans-=lv[r[y]]+rv[l[x]];
lv[l[x]]--,rv[r[y]]--;
l[x]^=z,r[y]^=z;
lv[l[x]]++,rv[r[y]]++;
ans+=lv[r[y]]+rv[l[x]];
// if(l[x]==r[y]) --ans;
}
signed main(){
int n,k,p;
n=read(),k=read(),p=read();
lv[0]=rv[0]=n;
ans=n*n;
int x,y,z;
for(int i=1;i<=k;++i){
x=read(),y=read(),z=read();
change(x,y,z);
mp[(x-1)*n+y]=z;
}
while(p--){
int r1,r2,c1,c2;
r1=read(),c1=read(),r2=read(),c2=read();
change(r1,c1,mp[(r1-1)*n+c1]);
mp[(r2-1)*n+c2]=mp[(r1-1)*n+c1];
mp[(r1-1)*n+c1]=0;
change(r2,c2,mp[(r2-1)*n+c2]);
printf("%lld\n",(int)(n*n-ans));
}
return 0;
}