【luogu P3997】小喵喵的新家 / T2 / 扇形面积并(线段树)

小喵喵的新家 / T2 / 扇形面积并

题目链接:luogu P3997

题目大意

给你 n 个同心扇形,问你有多少面积至少被 k 个扇形覆盖。

思路

考虑你可以用线段树分治或直接暴力维护得出每个时刻有哪些扇形覆盖。
(因为是环,所以有一些无法直接统计的你可以把它拆开成两个扇形)

然后你再想 k 个扇形代表什么,其实就是要找一个小块半径第 k 大的扇形。
那就维护一个线段树,每个半径的扇形出现了多少次,然后想平衡树一样跑就可以跑出第 k 大了。

代码

#include<cstdio>
#include<algorithm>
#define ll long long

using namespace std;

const int N = 1000005;
struct XDtree {//线段树
	int val[N << 4];
	
	void up(int now) {
		val[now] = val[now << 1] + val[now << 1 | 1];
	}
	
	void insert(int now, int l, int r, int pl, int va) {
		if (l == r) {
			val[now] += va;
			return ;
		}
		int mid = (l + r) >> 1;
		if (pl <= mid) insert(now << 1, l, mid, pl, va);
			else insert(now << 1 | 1, mid + 1, r, pl, va);
		up(now);
	}
	
	int query_kth(int now, int l, int r, int k) {//不要搞成第 k 小
		if (k > val[now]) return 0;
		if (l == r) {
			return l;
		}
		int mid = (l + r) >> 1;
		if (k > val[now << 1 | 1]) return query_kth(now << 1, l, mid, k - val[now << 1 | 1]);
			else return query_kth(now << 1 | 1, mid + 1, r, k); 
	}
}T;
int n, m, k, x, y, z, qn;
ll ans;
struct cz {
	int op, x, z;
}q[400001];

bool cmp(cz x, cz y) {
	if (x.x != y.x) return x.x < y.x;
	return x.op > y.op;
}

int main() {
	scanf("%d %d %d", &n, &m, &k);
	for (int i = 1; i <= n; i++) {
		scanf("%d %d %d", &x, &y, &z);
		if (y == m && z == -m) swap(y, z);
		y += m; z += m;
		if (y < z) q[++qn] = (cz){1, y + 1, x}, q[++qn] = (cz){-1, z, x};
			else {//可能要把扇形搞成两半
				q[++qn] = (cz){1, y + 1, x}; q[++qn] = (cz){-1, 2 * m, x};
				q[++qn] = (cz){1, 1, x}; q[++qn] = (cz){-1, z, x};
			}
	}
	
	sort(q + 1, q + qn + 1, cmp);
	int now = 1;
	for (int i = 0; i <= 2 * m; i++) {
		while (now <= qn && q[now].x == i && q[now].op == 1) {//把当前时刻开始的扇形加入
			T.insert(1, 1, 100000, q[now].z, q[now].op);
			now++;
		}
		int tmp = T.query_kth(1, 1, 100000, k);
		ans += 1ll * tmp * tmp;
		while (now <= qn && q[now].x == i && q[now].op == -1) {//把当前时刻为末尾的扇形删去
			T.insert(1, 1, 100000, q[now].z, q[now].op);
			now++;
		}
	}
	printf("%lld", ans);
	
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值