zoukankan      html  css  js  c++  java
  • CCF 201609-5 祭坛

    问题描述

    试题编号: 201609-5
    试题名称: 祭坛
    时间限制: 2.0s
    内存限制: 256.0MB
    问题描述:
    问题描述
      在遥远的Dgeak大陆,生活着一种叫做Dar-dzo-nye的怪物。每当这种怪物降临,人们必须整夜对抗怪物而不能安睡。为了乞求这种怪物不再降临,人们决定建造祭坛。
      Dgeak大陆可以看成一个用平面直角坐标系表示的巨大平面。在这个平面上,有 n 个Swaryea水晶柱,每个水晶柱可以用一个点表示。
      如果 4 个水晶柱依次相连可以构成一个四边形,满足其两条对角线分别平行于 x 轴和 y 轴,并且对角线的交点位于四边形内部(不包括边界),那么这 4 个水晶柱就可以建立一个结界。其中,对角线的交点称作这个结界的中心。
      例如下左图中,水晶柱 ABCD 可以建立一个结界,其中心为 O。

      为了起到抵御Dar-dzo-nye的最佳效果,人们会把祭坛修建在最多层结界的保护中。其中不同层的结界必须有共同的中心,这些结界的边界不能有任何公共点,并且中心处也不能有水晶柱。这里共同中心的结界数量叫做结界的层数。
      为了达成这个目的,人们要先利用现有的水晶柱建立若干个结界,然后在某些结界的中心建立祭坛。
      例如上右图中,黑色的点表示水晶柱(注意 P 和 O 点不是水晶柱)。祭坛的一个最佳位置为 O 点,可以建立在 3 层结界中,其结界的具体方案见下左图。当然,建立祭坛的最佳位置不一定是唯一,在上右图中,O 点左侧 1 单位的点 P 也可以建立一个在 3 层结界中的祭坛,见下右图。


      现在人们想知道:
      1. 祭坛最佳选址地点所在的结界层数;
      2. 祭坛最佳的选址地点共有多少个。
    输入格式
      输入的第一行包含两个正整数 n,q,表示水晶柱的个数和问题的种类。保证 q=1 或 2,其意义见输出格式。
      接下来 n 行,每行包含两个非负整数 x,y,表示每个水晶柱的坐标。保证相同的坐标不会重复出现。
    输出格式
      若 q=1,输出一行一个整数,表示祭坛最多可以位于多少个结界的中心;若 q=2,输出一行一个整数,表示结界数最多的方案有多少种。
    样例1输入
      26 1
      0 5
      1 1
      1 5
      1 9
      3 5
      3 10
      4 0
      4 1
      4 2
      4 4
      4 6
      4 9
      4 11
      5 0
      5 2
      5 4
      5 8
      5 9
      5 10
      5 11
      6 5
      7 5
      8 5
      9 10
      10 2
      10 5
    样例1输出
      3
    样例2输入
      26 2
      0 5
      1 1
      1 5
      1 9
      3 5
      3 10
      4 0
      4 1
      4 2
      4 4
      4 6
      4 9
      4 11
      5 0
      5 2
      5 4
      5 8
      5 9
      5 10
      5 11
      6 5
      7 5
      8 5
      9 10
      10 2
      10 5
    样例2输出
      2
    样例说明
      样例即为题目描述中的例子,两个样例数据相同,分别询问最多的结界数量和达到最多结界数量的方案数。
      其中图片的左下角为原点,右和上分别是 x 轴和 y 轴的正方向,一个格子的长度为单位长度。
      以图中的 O 点建立祭坛,祭坛最多可以位于 3 个结界的中心。不存在更多结界的方案,因此样例1的答案为 3。
      在 O 点左侧 1 单位的点 (4,5) 也可以建立一个在 3 个结界中的祭坛,因此样例2的答案为 2。
    评测用例规模与约定
      对于所有的数据,保证存在至少一种方案,使得祭坛建造在至少一层结界中,即不存在无论如何祭坛都无法建造在结界中的情况。
      数据分为 8 类,各类之间互相没有交集,分别有以下特点:
      1. 占数据的 10%,n=200,x,y≤n;
      2. 占数据的 10%,n=200,x,y≤109
      3. 占数据的 10%,n=1000,x,y≤n;
      4. 占数据的 10%,n=1000,x,y≤109
      5. 占数据的 10%,n=5000,x,y≤n;
      6. 占数据的 10%,n=5000,x,y≤109
      7. 占数据的 20%,n=300000,x,y≤n;
      8. 占数据的 20%,n=300000,x,y≤109

      此外,每类数据中,q=1 与 q=2 各占恰好一半。

    80分的做法(引自这里

    主要思路:

    先考虑一个比较简单的情况:

    给定一条横线上的点(a,b0),(a,b1)...(a,bn),以及一条竖线上的点(a0,b),(a1,b)...(an,b),求两条线能组成的最多结界数。

    很容易找出做法:

    1)找出两条线的交点(a,b)

    2)从交点向外的四个方向(上下左右)上最少的点的数量就是最多结界数。

    可以看出上面的想法是正确的。(一种构造方法是,将四个方向上离(a,b)最近的四个点连起来成为一个屏障,第二近的组成一个屏障,第三近的...)

    这个可以通过二分查找确定出来。

    知道了以上的简单情况,复杂的情况就好说了。。复杂的情况就是很多根竖线和很多根横线相交的问题。直观的想法就是,每根横线和每个竖线都测一下可以做多少结界。然后找出最大的并计数即可。但是这样有一点浪费时间,举个例子,如果我们得到的最大结界数是n,那么任何有少于2n-1的横线是没有必要再检查了(产生n个结界至少要有2n个点)。所以每当得到一个更大的结界数,我们就可以做一次更新,把那些不可能的线段剔除掉,这样可以节约一点时间。

    //80
    #pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
    #include<vector>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    #define pb emplace_back
    const int N=3e5+5;
    template<typename T>
    inline void read(T &x){
        register bool f=0;register char ch=getchar();x=0;
        for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1;
        for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
        if(f) x=-x;
    }
    template<typename T,typename...Args>
    void read(T &x,Args&...args){read(x);read(args...);}
    int n,Q,cnt=0,ans=0,cur=0;
    struct node{
        int x,y;
        node(){}
        node(int x,int y):x(x),y(y){}
        bool operator <(const node &a)const{
            return x!=a.x?x<a.x:y<a.y;
        }
    }a[N],b[N];
    vector<vector<node> >c(1),d(1);
    void deal(node (&a)[N],vector<vector<node> > &v){
        v[cnt=0].pb(a[0]);
        for(int i=1;i<n;i++){
            if(a[i].x==a[i-1].x){
                v[cnt].pb(a[i]);
            }
            else{
                if(v[cnt].size()==1){
                    v[cnt][0]=a[i];
                }
                else{
                    v.pb(vector<node>());
                    v[++cnt].pb(a[i]);
                }
                
            }
        }
    }
    inline bool comp(const vector<node> &a,const vector<node> &b){
        return a.size() > b.size();
    }
    inline int getlr(vector<node> &c,int key){
        if(key<=c[0].y) return 0;
        int l=0,r=c.size(),mid;
        while(l<r-1){
            mid=(l+r)>>1;
            if(c[mid].y==key) return 0;
            if(c[mid].y<key)
                l=mid;
            else
                r=mid;
        }
        return min(l+1,int(c.size()-l-1));
    }
    int main(){
        read(n,Q);
        for(int i=0,x,y;i<n;i++) read(x,y),a[i]=node(x,y);
        sort(a,a+n);
        
        for(int i=0;i<n;i++) b[i]=node(a[i].y,a[i].x);
        sort(b,b+n);
        
        deal(a,c);deal(b,d);
        sort(c.begin(),c.end(),comp);
        sort(d.begin(),d.end(),comp);
        for(int i=0;i<c.size();i++){
            for(int j=0;j<d.size();j++){
                int x=c[i][0].x;
                int y=d[j][0].x;
                cur=min(getlr(c[i],y),getlr(d[j],x));
                if(ans<cur){
                    ans=cur;
                    cnt=1;
                    while(!c.empty()&&c.back().size()<(ans*2)) c.pop_back();
                    while(!d.empty()&&d.back().size()<(ans*2)) d.pop_back();
                }
                else if(ans==cur){
                    cnt++;
                } 
            }
        }
        printf("%d
    ",Q==1?ans:cnt);
        return 0;
    }
  • 相关阅读:
    机器学习入门之二:一个故事说明什么是机器学习(转载)
    机器学习入门之一:背景介绍(转载)
    python 读取CSV文件 中文乱码
    如何快速学习一门新技术(转载)
    Oracle12c多租户如何启动关闭CDB或PDB (PDB自动启动)
    oracle单实例12.2.0.1安装
    PRVF-0002 : could not retrieve local node name
    图形化升级单机oracle 11.2.0.4 到 12.2.0.1
    ORA-00845: MEMORY_TARGET not supported on this system
    行转列、列转行
  • 原文地址:https://www.cnblogs.com/shenben/p/12500210.html
Copyright © 2011-2022 走看看