链接:http://poj.org/problem?id=2464
题意:平面上有若干点,stan通过一个点划了一条直线,ollie通过在这条垂直直线上的点作了一条水平线,平面被分成4个象限,定义stan获得的分数为1,3象限的点的数量,ollie获得的分数为2,4象限上的点的数量,线上的点不归任何人所有。两人都采用最优的策略使自己获得的点数最大。由于stan的选择具有主动权,为了对自己有利,求stan所能够获得的最小的可能的分数最大,输出该分数,并输出ollie可能的分数。
思路:首先分析可以得到:stan画的线上如果没有其他点,那么ollie的选择只有一个。如果有多个点,ollie会选择自己分数高的点,如果相同,选择stan分数低的点。
实现:
定义right[i],第i个点的正右方向,left[i],第i个点的负左方向,up[i]第i个点的正上方向,down[i]第i个点的负下方向。并且定义:TR,TL,BL,BR为第一象限上的点。
首先对y升序排序(y相同按x升序排序),获得对large(反向),left(正向),right(反向)的统计。由于题目中没有明确的坐标范围,因此需要对点进行离散化,并且排序操作已经已经使得一个点对的一维操作已经有序,那么我们只要离散此时的y值,得到原来的下标对应的按x排序后的下标。为了得到up,down的统计值,然后再对x升序排序(x相同升序y)。如此就有我们所需要的所有数据。设定一条垂直线,水平从第一个点向右移动,得到最小的没有被访问过的点的横坐标的原来的编号对应的通过y离散化后的编号,,用树状数组维护左下的点数,计算并统计更新答案。
代码:
#include<cstdio>
#include<string.h>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=200005;
int c[maxn];
int up[maxn];
int down[maxn];
int right[maxn];
int left[maxn];
int large[maxn];
int id[maxn];
int n;
int lowbit(int i){return i&(-i);}
void add(int x,int v){
while(x<maxn){
c[x]+=v;
x+=lowbit(x);
}
}
int getsum(int x){
int res=0;
while(x>0){
res+=c[x];
x-=lowbit(x);
}
return res;
}
struct node{
int x,y,id;
}p[maxn];
void init(void){
for(int i=0;i<=n;i++){
c[i]=large[i]=up[i]=down[i]=left[i]=right[i]=0;
}
}
int cmp1(node a,node b){
if(a.x!=b.x)return a.x<b.x;
return a.y<b.y;
}
int cmp2(node a,node b){
if(a.y!=b.y)return a.y<b.y;
return a.x<b.x;
}
int main(){
while(~scanf("%d",&n),n){
for(int i=0;i<n;i++){
scanf("%d%d",&p[i].x,&p[i].y);
p[i].id=i;
}
init();
sort(p,p+n,cmp2);//先对y排序
for(int i=1;i<n;i++){
if(p[i].y==p[i-1].y ){
left[p[i].id ]=left[p[i-1].id ]+1;
}
if(p[n-i-1].y==p[n-i].y ){
large[p[n-i-1].id]=large[p[n-i].id];
right[p[n-i-1].id]=right[p[n-i].id]+1;
}else{
large[p[n-i-1].id]=i;
}
}
int k=1;
id[p[0].id ]=k;
for(int i=1;i<n;i++){//对y离散化
if(p[i].y==p[i-1].y){
id[p[i].id]=k;
}else{
id[p[i].id]=++k;
}
}
sort(p,p+n,cmp1);
for(int i=1;i<n;i++){
if(p[i].x==p[i-1].x){
down[p[i].id ]=down[p[i-1].id]+1;
}
if(p[n-i-1].x==p[n-i].x){
up[p[n-i-1].id]=up[p[n-i].id ]+1;
}
}
int px=p[0].x;//垂直线
set<int>st;
int s_min_max=0;
for(int i=0;i<n;i++){
int omin=maxn;
int omax=0;
while(i<n&&p[i].x==px){//竖直线上的所有的点
int yid=id[p[i].id];//离散化后的点
int BL=getsum(yid)-left[p[i].id ]-down[p[i].id ];
int TL=i-BL-left[p[i].id ]-down[p[i].id ];
int TR=large[p[i].id ]-TL-up[p[i].id ];
int BR=n-1-TL-TR-BL-up[p[i].id ]-left[p[i].id ]-right[p[i].id ]-down[p[i].id ];
add(yid,1);
int ollie=TL+BR;
if(omax<ollie){
omax=ollie;
omin=TR+BL;
}else if(omax==ollie){
omin=min(omin,TR+BL);
}
i++;
}
if(i<n)px=p[i--].x;
if(omin>s_min_max){
s_min_max=omin;
st.clear ();
st.insert (omax);
}else if(omin==s_min_max){
st.insert (omax);
}
}
printf("Stan: %d; Ollie:",s_min_max);
set<int>::iterator it;
for(it=st.begin ();it!=st.end();it++){
printf(" %d",*it);
}
printf(";\n");
}
}