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;
} 
posted @ 2022-07-18 21:10  _yolanda  阅读(57)  评论(0编辑  收藏  举报