JZOJ4925. 【GDOI2017模拟12.18】稻草人

题意:

YLOI村有一片荒地,上面竖着 N N N个稻草人,村民们每年多次在稻草人们的周围举行祭典。
有一次,YLOI村的村长听到了稻草人们的启示,计划在荒地中开垦一片田地。和启示中的一样,田地需要满足以下条件:
1、田地的形状是边平行于坐标轴的长方形;
2、左下角和右上角各有一个稻草人;
3、田地的内部(不包括边界)没有稻草人。
给出每个稻草人的坐标,请你求出有多少遵从启示的田地的个数。

数据范围:

1 ≤ N ≤ 2 ∗ 1 0 5 1 \leq N \leq 2*10^5 1N2105
0 ≤ X i ≤ 1 0 9 ( 1 ≤ i ≤ N ) 0 \leq X_i \leq 10^9(1 \leq i \leq N) 0Xi109(1iN)
0 ≤ Y i ≤ 1 0 9 ( 1 ≤ i ≤ N ) 0 \leq Y_i \leq 10^9(1 \leq i \leq N) 0Yi109(1iN)
X i ( 1 ≤ i ≤ N ) X_i(1 \leq i \leq N) Xi(1iN)互不相同。
Y i ( 1 ≤ i ≤ N ) Y_i(1 \leq i \leq N) Yi(1iN)互不相同。

Analysis:

这题考场 y y yy yy了一个和正解不太一样的做法。
正解是利用了单调栈,而我不是,但貌似是本质相同的。
这种题考虑成为答案的条件,若选了一对 i , j i,j i,j,那么就是要求。
对于不存在一个点 k k k满足, X k ∈ ( X i , X j ) 且 Y k ∈ [ Y i , Y j ] X_k \in {(X_i,X_j)} 且 Y_k \in {[Y_i,Y_j]} Xk(Xi,Xj)Yk[Yi,Yj]
那也就是限制了一个取值范围,这种东西直接统计很难,考虑一下分治。
先对每个点按横坐标排个序。
分治的话肯定要预处理一些东西来优化,我们考虑对左边每个点预处理出它到分治中心,所有纵坐标大于它的点的最小纵坐标,相当于把矩形的上界压下来,表示右上角至多那么高。
对于右边每个点预处理出类似的东西,压矩形的下界,表示左下角至少那么高。
然后考虑,若两个对于一对在左右两边的点 i , j i,j i,j,若 i i i对于 j j j满足下界限制, j j j对于 i i i满足上界限制,那么他们就是可行的一堆点。这个东西表现出来就是区间交,排个序树状数组统计一下就行了,常数有点大。复杂度 O ( n log ⁡ 2 n ) O(n \log^2{n}) O(nlog2n)

Code:

# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
struct P
{
	int x,y,ty;
}p[N],a[N];
int t[N],c[N],mx[N],mi[N];
int n;
ll ans;
bool cmp(P a,P b) { return a.x == b.x ? a.ty > b.ty : a.x < b.x; }
inline void add(int x,int w) { for (int i = x ; i <= n ; i += i & (-i)) t[i] += w; }
inline void add1(int x,int w) { for (int i = x ; i <= n ; i += i & (-i)) mx[i] = max(mx[i],w);}
inline void add2(int x,int w) { for (int i = x ; i <= n ; i += i & (-i)) mi[i] = min(mi[i],w); }
inline int qry(int x)
{
	int ret = 0;
	for (int i = x ; i ; i -= i & (-i)) ret += t[i];
	return ret;
}
inline int qry1(int x)
{
	int ret = 1;
	for (int i = x ; i ; i -= i & (-i)) ret = max(ret,mx[i]);
	return ret;
}
inline int qry2(int x)
{
	int ret = n;
	for (int i = x ; i ; i -= i & (-i)) ret = min(ret,mi[i]);
	return ret;
}
inline void clear(int x) { for (int i = x ; i <= n ; i += i & (-i)) mx[i] = 1; }
inline void clear1(int x) { for (int i = x ; i <= n ; i += i & (-i)) mi[i] = n; }
inline void solve(int l,int r)
{
	if (l == r) return;
	int mid = (l + r) >> 1,h = 0;
	for (int i = mid ; i >= l ; --i) a[++h] = (P){p[i].y,qry2(n - p[i].y + 1),0},add2(n - p[i].y + 1,p[i].y);
	for (int i = mid + 1 ; i <= r ; ++i) a[++h] = (P){qry1(p[i].y),p[i].y,1},add1(p[i].y,p[i].y);
	for (int i = mid ; i >= l ; --i) clear1(n - p[i].y + 1);
	for (int i = mid + 1 ; i <= r ; ++i) clear(p[i].y);
	sort(a + 1,a + h + 1,cmp);
	for (int i = 1 ; i <= h ; ++i)
	if (!a[i].ty) ans += qry(a[i].y) - qry(a[i].x - 1);
	else add(a[i].y,1);
	for (int i = 1 ; i <= h ; ++i) if (a[i].ty) add(a[i].y,-1);
	solve(l,mid); solve(mid + 1,r);
}
int main()
{
//	freopen("a.in","r",stdin);
	freopen("scarecrows.in","r",stdin);
	freopen("scarecrows.out","w",stdout);
	scanf("%d",&n);
	for (int i = 1 ; i <= n ; ++i) scanf("%d%d",&p[i].x,&p[i].y),c[i] = p[i].y,mx[i] = 1,mi[i] = n;
	sort(p + 1,p + n + 1,cmp),sort(c + 1,c + n + 1);
	for (int i = 1 ; i <= n ; ++i) p[i].y = lower_bound(c + 1,c + n + 1,p[i].y) - c;
	solve(1,n);
	printf("%lld\n",ans);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值