「JOISC 2014 Day3」稻草人
题意
在一个二维平面上,给定点集 $S$,求所有满足
- $\large{x_i\lt x_j}$;
- $\large{y_i\lt y_j}$;
- $\large{\forall k\in S\cap\{i,j\}}$,有 $\large{x_k\notin[x_i,x_j],y_k\notin [y_i,y_j]}$;
的点对 $(i,j)$ 的数量。
Solution
发现对于一个点 $i$ 而言,对答案的贡献应为在它右上方的点所形成的凸包的大小。即其右上方 $x$ 最接近于 $x_i$ 和 $y$ 最接近于 $y_i$ 的两个点之间形成的类下凸包大小。令二者分别为 $pre_i$ 和 $r_i$。
形式化地,$\forall k\in T_i$,有 $x_i\lt x_k,y_i\lt y_k$。
则 $pre_i \in T_i$,$\forall k \in T_i$,有 $\lvert x_{pre_{j}}-x_i\lvert \leq \lvert x_k-x_i\lvert $。
同样地,有 $r_i \in T_i$,$\forall k \in T_i$,有 $\lvert y_{r_{j}}-y_i\lvert \leq \lvert y_k-y_i\lvert $。
则对答案可以产生贡献的点集为 $pre_i,a_1,\cdots,a_p,r_i$,满足 $a_i\in T_i$,$x_{a_{i-1}}\lt x_{a_i}\lt x_{a_{i+1}}$,且 $y_{a_{i-1}}\lt y_{a_i}\lt y_{a_{i+1}}$。
对于每个数,单调栈维护类下凸包即可,时间复杂度为 $\mathcal{O}(n^2)$。
考虑优化,发现不必要对于每个点重新计算答案,考虑对于 $x$ 或 $y$ 区间分治并合并答案。以分治 $x$ 坐标为例,从 $y_{max}$ 向下不断枚举。对于 $[mid+1,right]$ 区间,维护一个类下凸包,每个点满足在前一个点的右下方。对于 $[left,mid]$ 区间,也维护一个类下凸包,每个点满足在前一个点的左下方,对于新加入的点 $i$,在类下凸包中的前一个点对 $i$ 对答案贡献的影响最大,即前文提到的 $pre_i$。在右区间的类下凸包中,所有 $y\ge y_{pre_{i}}$ 的点都不产生贡献了,可以在右区间的单调栈中二分找到分界。
时间复杂度为 $\mathcal{O}(n\log^2n)$,可以通过本题。
code
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int res=0,flag=1;
char ch=getchar();
while(!isalnum(ch)) (ch=='-')?flag=-1:1,ch=getchar();
while(isalnum(ch)) res=res*10+ch-'0',ch=getchar();
return res*flag;
}
struct Stack
{
int head;
std::pair<int,int> val[200010];
void push(pair<int,int> value)
{
val[++head]=value;
return ;
}
void pop() { head--; return ; }
pair<int,int> top() { return val[head]; }
bool empty() { return !head; }
void clear() { head=0; return ; }
int solve(int pos)
{
int left=1,right=head,res=head+1;
while(left<=right)
{
int mid=(left+right)>>1;
if(val[mid].second<pos)
res=mid,right=mid-1;
else
left=mid+1;
}
if(res==head+1)
return 0;
return head-res+1;
}
};
struct Stack a,b;
int n;
long long ans;
void solve(int left,int right,vector<pair<int,int> > val)
{
b.push(make_pair(0x3f3f3f3f,0x3f3f3f3f));
if(left==right)
{
ans+=val.size()-1;
return ;
}
int mid=(left+right)>>1;
vector<pair<int,int> > l,r;
l.clear(),r.clear();
for(auto i=val.begin();i!=val.end();i++)
{
if(i->first<=mid)
{
l.push_back(*i);
while(b.empty()==false&&b.top().first<i->first)
b.pop();
ans+=a.solve(b.top().second);
b.push(*i);
}
if(i->first>mid)
{
r.push_back(*i);
while(a.empty()==false&&a.top().first>=i->first)
a.pop();
a.push(*i);
}
}
a.clear(),b.clear();
if(l.size()>=1) solve(left,mid,l);
if(r.size()>=1) solve(mid+1,right,r);
return ;
}
int main(int argc,const char *argv[])
{
n=read();
int left=INT_MAX,right=0;
vector<pair<int,int> > val;
for(int i=1;i<=n;i++)
{
int x=read(),y=read();
val.push_back(make_pair(x,y));
left=std::min(left,x);
right=std::max(right,x);
}
std::sort(val.begin(),val.end(),[](pair<int,int> a,pair<int,int> b){return (a.second==b.second)?(a.first>b.first):(a.second>b.second);});
solve(left,right,val);
printf("%lld\n",ans);
return 0;
}

浙公网安备 33010602011771号