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;
}

 

posted @ 2019-11-02 14:45  逆天峰  阅读(148)  评论(0编辑  收藏  举报
作者:逆天峰
出处:https://www.cnblogs.com/gcfer//