zoukankan      html  css  js  c++  java
  • P2906 [USACO08OPEN]牛的街区Cow Neighborhoods

    传送门

    曼哈顿距离好像不好直接算,我们可以把牛的坐标转化一下以方便计算距离

    (x,y) --> (x+y,x-y) 那么距离就可以表示成 $max(left |x_1-x_2  ight |,left | y_1-y_2 ight |)$

    自己在草稿纸上算一下就知道了,(因为之后我们会按转化后的横坐标排序,所以式子会少一种情况)

    (以下横纵坐标均已转化)

    所以可以考虑这样一种方案,把牛按横坐标排序

    然后考虑总左到右枚举牛加入队列:每次加入一只牛,与队列里的其他牛比较一下纵坐标距离,这样能够保证每只牛都两两匹配过

    并且队列保证当前的牛的横坐标与队列内其他牛的横坐标之差不大于C(即与队列最左的牛横坐标之差不大于C)

    但是复杂度会爆炸,考虑优化

    可以发现,我们匹配时只要找纵坐标大于它的最小的牛和纵坐标小于它的最大的牛,因为如果它能匹配队列的其他牛A,那么那两只牛也一定至少有一只能匹配A

    就是在队列里求一个值的前驱后继,那么很容易想到用平衡树 multiset 来维护

    可能会有疑问此时的前驱后继是可以和原数相同的(转化后的横纵坐标可能相同),如果强行维护好像很麻烦的样子

    但是对于相同的点显然前驱后继至少有一个会考虑到,那么对答案就不会有影响了

    然后并查集维护联通块就好了

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<set>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=1e5+7;
    int n,C;
    int fa[N],cnt[N];//cnt是当前每个块的大小
    inline int find(int x) { return x==fa[x] ? x : fa[x]=find(fa[x]); }
    inline void uni(int x,int y)//并查集合并两个块
    {
        int xa=find(x),xb=find(y);
        if(xa==xb) return;
        fa[xa]=xb; cnt[xb]+=cnt[xa]; cnt[xa]=0;
    }
    struct data//存转化后的横纵坐标
    {
        int x,y,id;
        data () { x=y=id=0; }
        inline bool operator < (const data &tmp) const {//multiset内按纵坐标排序
            return y<tmp.y;
        }
    }d[N];
    inline bool cmp(const data &a,const data &b){ return a.x<b.x; }//按横坐标排序
    int fir;//队列的最左边位置
    multiset <data> s;
    multiset <data>::iterator it;
    void slove()
    {
        sort(d+1,d+n+1,cmp);//排序
        fir=1; s.insert(d[1]);//第一个直接加进去
        data t; t.y=2e9+7; s.insert(t); t.y=-(2e9+7); s.insert(t);//防止指针越界
        for(int i=2;i<=n;i++)
        {
            while(d[i].x-d[fir].x>C) s.erase(s.find(d[fir])),fir++;//更新队列
            it=s.lower_bound(d[i]);//找后继
            t=*it; --it;//找前驱
            if(t.y-d[i].y<=C) uni(t.id,d[i].id);//尝试与后继合并
            t=*it;
            if(d[i].y-t.y<=C) uni(t.id,d[i].id);//尝试与前驱合并
            s.insert(d[i]);//别忘了加到multiset里
        }
    }
    int main()
    {
        int a,b;
        n=read(); C=read();
        for(int i=1;i<=n;i++)
        {
            a=read(),b=read();
            d[i].x=a+b; d[i].y=a-b; d[i].id=i;
            fa[i]=i; cnt[i]=1;//初始化
        }
        slove();
        int mx=0,tot=0;//计算块和最大块大小
        for(int i=1;i<=n;i++)
            if(cnt[i])
            {
                mx=max(mx,cnt[i]);
                tot++;
            }
        printf("%d %d",tot,mx);
        return 0;
    }
  • 相关阅读:
    weekly review 200908: Talk Show of ASP.NET
    weeklyreview 200911: Drowse of Spring
    数据库中标识种子(否,是,是(不用于复制))解释
    Hashtable.ContainsKey跟Hashtable.Contains的区别
    【Oracle学习起步1】用户解锁及密码输入问题
    C#弹出对话框实现
    因为文件组 'PRIMARY' 已满。
    SQL删除数据的各种方式总结
    C standard library contents
    scanf("%c",&c)前的printf函数调用问题
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9860270.html
Copyright © 2011-2022 走看看