10 01模拟赛订正
好吧,这是我第一次写模拟赛的订正,主要是有时间而且这次的题确实好...
第一题确实好,用的算法人人都会,就是看你能不能想到,我考只打了O(n^4)的暴力,最后还苦逼的MLE,爆零了...
暴力就不多说了...枚举两个点更新其他的点...
其实我考场上思考的是,能被标记的点都与其他的点有什么联系,可惜,除了模拟题目的做法,就不会了...
那让我们就认真地思考一发:我们设A(x1,x2),B(x2,c2),C(x3,c3)来更新D点,只有:有两个点横坐标相等,有两个点纵坐标相等,才可以更新出来一个新的点...
那我们看更新之后又出现了什么结果,还是有两个点横坐标相等,有两个点纵坐标相等,这是就要思考了或许我们不必枚举点,而应该在行列之间大做文章...
之后,根据网格题的经验,我们就连边,即点A被标记,那我们将行列连边,之后,我们在手玩一下A,B,C点就会发现,点D的行列已经联通了...而这就用到了并查集...
其实我们再想想就知道为什么会是这样的,因为每个被标记的点都其实提供一种行与列的关系,当两个对角线的点出现时,目标点的x与y都已经被代表,而此时再出现一个顶点,就将目标点的行列联通了...
而这里有体验到了并查集的魅力,即维护关系的合并与查询,而这种关系则需要我们慧眼挖掘...
还有网格题的行列建图则也是一种好的思路,因为图论中大量的算法我们就能利用了...
#include<bits/stdc++.h> using namespace std; const int N=5100; int f[N*2],n,m,q,sum[N][N]; inline int read() { int x=0,ff=1; char ch=getchar(); while(!isdigit(ch)){if(ch=='-') ff=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*ff; } inline int getf(int k) {return f[k]==k?k:(f[k]=getf(f[k]));} int main() { freopen("grid.in","r",stdin); freopen("grid.out","w",stdout); n=read();m=read();q=read(); for(register int i=1;i<=n*2;++i) f[i]=i; for(register int i=1;i<=m;++i) { int x=read(),y=read()+n;//将列+n int t1=getf(x); int t2=getf(y); if(t1!=t2) f[t1]=t2; } for(register int i=1;i<=n;++i) for(register int j=1;j<=n;++j) { int t1=getf(i); int t2=getf(j+n); if(t1==t2) sum[i][j]++; sum[i][j]+=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]; } for(register int i=1;i<=q;++i) { int r1=read(),c1=read(); int r2=read(),c2=read(); printf("%d\n",sum[r2][c2]-sum[r2][c1-1]-sum[r1-1][c2]+sum[r1-1][c1-1]); } return 0; }