AT1225 かかし

好题。

考虑 cdq 分治维护偏序。

按照横坐标递增排序,再按照纵坐标递减归并。

考虑左半边和右半边的点对,对于左半边合法点 pp,将右半边纵坐标 yp\geq y_p 的点加入单调栈,同时维护横坐标单增。

对于左半边内部的影响,另用一个单调栈维护,横坐标递减,答案累加时二分右边栈内纵坐标小于左边栈顶纵坐标个数即可。

左边栈:yy 单减,xx 单减。

右边栈:yy 单减,xx 单增。

时间复杂度 O(nlog2n)\mathcal O(n\log^2n),这里偷懒用 sort 导致多了个 log\log

#include<bits/stdc++.h>

using namespace std;

#define int long long
typedef long long ll;

#define ha putchar(' ')
#define he putchar('\n')

inline int read()
{
	int x = 0, f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-')
			f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		x = x * 10 + c - '0', c = getchar();
	return x * f;
}

inline void write(ll x)
{
	if(x < 0)
	{
		putchar('-');
		x = -x;
	}
	if(x > 9)
		write(x / 10);
	putchar(x % 10 + '0');
}

const int _ = 2e5 + 10;

int n;

ll ans;

struct abc
{
	int x, y;
} k[_], t[_];

int t1, s1[_], t2, s2[_];

bool cmp1(abc a, abc b) {return a.x < b.x;}
bool cmp2(abc a, abc b) {return a.y > b.y;}

int erfen(int p)
{
	int l = 0, r = t2;
	while(l < r)
	{
		int mid = (l + r + 1) >> 1;
		if(k[s2[mid]].y > p) l = mid;
		else r = mid - 1;
	}
	return l;
}

void cdq(int l, int r)
{
	if(l == r) return;
	int mid = (l + r) >> 1;
	cdq(l, mid), cdq(mid + 1, r);
	sort(k + l, k + mid + 1, cmp2);
	sort(k + mid + 1, k + r + 1, cmp2);
	int j = mid + 1; t1 = t2 = 0;
	for(int i = l; i <= mid; ++i)
	{
		while(j <= r && k[i].y < k[j].y)
		{
			while(t2 && k[s2[t2]].x > k[j].x) t2--;
			s2[++t2] = j, j++;
		}
		while(t1 && k[s1[t1]].x < k[i].x) t1--;
		ans += t2;
		if(t1) ans -= erfen(k[s1[t1]].y);
		s1[++t1] = i;
	}
}

signed main()
{
	n = read();
	for(int i = 1; i <= n; ++i)
		k[i].x = read(), k[i].y = read();
	sort(k + 1, k + n + 1, cmp1);
	cdq(1, n);
	write(ans), he;
	return 0;
}

/*
10
2 1
3 0
6 3
10 2
16 4
0 8
8 12
11 14
14 11
18 10

15
*/
posted @ 2022-07-16 15:39  蒟蒻orz  阅读(1)  评论(0编辑  收藏  举报  来源