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;
}
原文地址:https://www.cnblogs.com/shenben/p/12500210.html