http://codeforces.com/gym/101350/problem/F
题意:有m个笑话,每个笑话的区间是[L, R],笑话种类有1e5,一开始所有猴子都在凳子上,听到一个笑话,就倒下,但是如果是听过的笑话,就重新回到凳子上。问最终有多少个猴子在凳子上。
相当于有1e5个线段,如果我们能知道第i个猴子,被多少个线段覆盖了,那么可以找出那些线段中的最后那一条,就是最后覆盖上去的那一条,那条线段是哪一个笑话,设为k,如果这个笑话覆盖这个猴子的次数 > 1,那么这个猴子将会回到凳子上。也就是只和最后一步有关
那么,假如有线段:
按左端点排序:[1, 100], [2, 2] ,一个扫描变量p1 = 1开始
按右端点排序:[2, 2], [1, 100], 一个扫描变量p2 = 1开始
就能很快判断到第i个猴子被那些线段覆盖了。
按顺序枚举每一个猴子i,比如i = 2
那么,把i >= segment1[p1].L的都表明这条线段能覆盖i
吧i > segment2[p2].R的都表明这条线段已经离开了i
然后统计即可。
#include <bits/stdc++.h> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; const int maxn = 2e5 + 20; struct Node { int L, R, id; }one[maxn], two[maxn]; bool cmp1(struct Node a, struct Node b) { return a.L < b.L; } bool cmp2(struct Node a, struct Node b) { return a.R < b.R; } int idForJoke[maxn]; int has[maxn]; set<int>ss; void work() { ss.clear(); memset(has, 0, sizeof has); int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= m; ++i) { int pos, joke, dis; scanf("%d%d%d", &pos, &joke, &dis); one[i].L = max(1, pos - dis), one[i].R = min(n, pos + dis), one[i].id = i; two[i] = one[i]; idForJoke[i] = joke; } sort(one + 1, one + 1 + m, cmp1); sort(two + 1, two + 1 + m, cmp2); int ans = 0, p1 = 1, p2 = 1; for (int i = 1; i <= n; ++i) { while (p1 <= m && i >= one[p1].L) { ss.insert(one[p1].id); has[idForJoke[one[p1].id]]++; ++p1; } while (p2 <= m && i > two[p2].R) { ss.erase(two[p2].id); has[idForJoke[two[p2].id]]--; ++p2; } if (ss.size()) { ans += has[idForJoke[*ss.rbegin()]] > 1; } else ans++; } printf("%d\n", ans); } int main() { #ifdef local freopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endif int t; scanf("%d", &t); while (t--) work(); return 0; }
时间: 2024-10-16 02:50:34