zstu4186——线段树——表白计划(未完成)
Description
众所周知,程序员是种神奇的没有妹子的生物。
有一个很厉害的程序员Doge,他没有妹子。于是他将自己心仪的n个妹子编号为1到n,然后制定了m个表白计划在接下来的t天内向妹子表白,对于某一个计划,他会在计划的第L天到第R天向编号为x的妹子每天表白一次。由于计划很多,有些表白区间可能有重复,所以他有可能在某一天跟同一个妹子表白很多次。他每一次表白被拒绝后都会收到被表白的妹子亲手写的好人卡,毫无悬念,他每一次都能收到好人卡。
现在Doge想知道在某些时间段他收到了多少张好人卡,还有这个时间段哪个妹子没有给过他好人卡,如果有多个妹子没有给过,他想知道编号最小的是哪个妹子。然而他正忙于制定下一个表白计划,所以请你这个程序员来帮下解决下吧!(PS:不要在意各种奇怪的事情,程序员本来就是个神奇的生物。)
Input
题目有多组测试数据。
对于每一组测试数据,第一行有三个数n, m, t(1 <= n, m, t <= 80000),在接下来的m行里,每一行有三个数L, R, x (1 <= L <= R <= t, 1 <= x <= n)表示Doge将在第L天到第R天每天向第x个妹子表白一次。接下来一行有一个数Q(1 <= Q <= 80000),接下来有Q行询问。对于询问,我们规定一个特殊的数z,z初始为0。每次询问给你两个数L, R (1 <= L<=R<=t),表示询问的区间为第L+z天到第R+z天,如果R+z超过t的话,就把区间变为第t-(R-L)天到第t天。对于每次询问,需要求出这段区间内Doge收到的好人卡个数和没有给过Doge好人卡的最小的妹子编号,如果所有妹子都给了好人卡,那么编号为0,然后把编号赋值给z。
Output
对于每次询问,输出为一行以一个空格间隔的两个数,第一个数为好人卡个数,第二个数为妹子编号。
Sample Input
5 5 10 1 3 1 3 5 2 7 9 3 2 4 4 8 10 5 4 1 6 6 8 1 9 4 6
Sample Output
9 3 5 1 14 0 3 1
HINT
询问包含两个部分,第一部分是求区间和,那么这个可以简单的通过两次前缀和搞定。 然后处理第二部分。 数据给出的是每个妹子发好人卡的时间段,借此可以得到每个妹子不发好人卡的时间段,容 易知道不发好人卡的时间段的总数也是O(m)的。 考虑对妹子们的下标[1,n]建一棵线段树。 设线段树上节点i对应的妹子区间为[li,ri],在节点i上保存妹子[li,ri]们不发好人卡的时间段。在 线段树上,包含妹子x的节点只有logn个,因此整棵线段树里最多包含mlogn个时间段。 接下来讲如何用这棵线段树处理询问(l,r):标号最小的不发好人卡的时间段完全包含[l,r]的妹 子是哪一个。 当走到线段树的节点i的时候,如果节点i的左儿子中存在完全包含[l,r]的时间段,那么往左儿 子走肯定是比右儿子要优的,因为左儿子包含的妹子们标号永远比右儿子小。否则就往右儿 子走。 接着处理子问题:怎样快速的判断线段树的一个节点中存的时间段们是否包含[l,r]。 我们来考虑对这个节点上的时间段们进行一些处理。设这些时间段们分别为[L1,R1],[L2,R2] ….[Lk,Rk],容易知道如果存在两个时间段a和b满足:La <= Lb 并且 Rb <= Ra,那么时间段 b就不需要存在了。那么对于这些只有相交/相离关系的线段,把他们按照Li从小到大排序 后,他们的Ri也会是升序的。那么接下来只要在这些线段间二分,就可以logn时间判断它们 是否包含[l,r]。
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <vector> using namespace std; typedef long long ll; const int N = 100000+5; vector<pair<int,int> > a[N], tmp, b[N]; ll sum[N]; struct Segtree { #define lson rt<<1, l, mid #define rson rt<<1|1, mid+1, r vector<pair<int,int> > node[N<<2]; void build(int rt, int l, int r) { node[rt].clear(); for(int i = l;i <= r; i++) { for(int j = 0;j < a[i].size(); j++) { node[rt].push_back(make_pair(a[i][j].first, a[i] [j].second)); } } sort(node[rt].begin(), node[rt].end()); int top = 1; for(int i = 1;i < node[rt].size(); i++) if(node[rt] [i].second > node[rt][top-1].second){ if(node[rt][i].first == node[rt][top-1].first) node[rt][top-1].second = node[rt][i].second; else node[rt][top++] = node[rt][i]; } node[rt].resize(top); if(l == r) return ; int mid = l+r>>1; build(lson); build(rson); } int query(int rt, int l, int r, int x, int y) { if(l == r) return l; int mid = l+r>>1; if(check(rt<<1, x, y)) return query(lson, x, y); return query(rson, x, y); } inline bool check(int rt, int x, int y) { int pos = lower_bound(node[rt].begin(), node[rt].end(), make_pair(x+1, 0))-node[rt].begin(); if(pos == 0 || node[rt][pos-1].second < y) return false; return true; } }tree; int n, m, t; int main() { //freopen("data.in", "r", stdin); //freopen("data.out", "w", stdout); while(scanf("%d%d%d", &n, &m, &t) != -1) { for(int i = 1;i <= t; i++) sum[i] = 0; for(int i = 1;i <= n; i++) b[i].clear(); int l, r, x; for(int i = 0;i < m; i++) { scanf("%d%d%d", &l, &r, &x); sum[l]++; sum[r+1]--; b[x].push_back(make_pair(l, r)); } for(int i = 1;i <= t; i++) sum[i] += sum[i-1]; for(int i = 1;i <= t; i++) sum[i] += sum[i-1]; for(int i = 1;i <= n; i++) { sort(b[i].begin(), b[i].end()); a[i].clear(); int cur = 1; for(int j = 0;j < b[i].size(); j++) { if(cur < b[i][j].first) a[i].push_back(make_pair(cur, b[i][j].first-1)); cur = max(cur, b[i][j].second+1); } if(cur <= t) { a[i].push_back(make_pair(cur, t)); } } tree.build(1, 1, n); int q, z = 0; scanf("%d", &q); while(q--) { scanf("%d%d", &l, &r); if(r+z <= t) l += z, r += z; else l = t-(r-l), r = t; if(tree.check(1, l, r)) z = tree.query(1, 1, n, l, r); else z = 0; printf("%lld %d\n", sum[r]-sum[l-1], z); } } return 0; }