小喵喵的新家 / 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;
}