一道线段树好题啊。
看到这道题后大概猜到是线段树,但是实在想不出来区间合并怎么写。最后还是学姐给我讲的。
首先都知道要把高度转化成斜率,然后明确的一点就是如果该点斜率比上一次选的大,就一定要选,否则一定不选。也就是说每一个区间都是一个单调上升的子序列(但是和平常理解的LIS不同)。
我们将斜率大小形象化为高度不一的线段,则两个区间的选取情况一定是这样的:
左右子区间都是一个单调递增的序列。合并完后的答案应该是左区间的全部和右区间高出左区间的Max那一部分。那么可以想到在右区间里二分找第一个大于Max的答案。然而这样需要把每一个区间的答案都存下来,空间开不下。因此我们二分的时候模仿线段树查询,在右区间的子区间里递归查找。
所以说区间合并是logn的,总复杂度O(nlog2n)。(神不神奇)
斜率为了防止double掉精度,用向量存。
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cctype> 8 #include<vector> 9 #include<stack> 10 #include<queue> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(' ') 14 #define Mem(a, x) memset(a, x, sizeof(a)) 15 #define rg register 16 typedef long long ll; 17 typedef double db; 18 const int INF = 0x3f3f3f3f; 19 const db eps = 1e-8; 20 const int maxn = 3e5 + 5; 21 inline ll read() 22 { 23 ll ans = 0; 24 char ch = getchar(), last = ' '; 25 while(!isdigit(ch)) last = ch, ch = getchar(); 26 while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar(); 27 if(last == '-') ans = -ans; 28 return ans; 29 } 30 inline void write(ll x) 31 { 32 if(x < 0) x = -x, putchar('-'); 33 if(x >= 10) write(x / 10); 34 putchar(x % 10 + '0'); 35 } 36 37 int n, m; 38 struct Vec 39 { 40 ll x, y; 41 bool operator < (const Vec& oth)const 42 { 43 return y * oth.x < oth.y * x; 44 } 45 }; 46 struct Tree 47 { 48 int l, r, sum; 49 Tree() {sum = 0;} 50 Vec vec; 51 }t[maxn << 2]; 52 53 void build(int L, int R, int now) 54 { 55 t[now].l = L; t[now].r = R; 56 t[now].vec = (Vec){R, 0}; 57 if(L == R) return; 58 int mid = (L + R) >> 1; 59 build(L, mid, now << 1); 60 build(mid + 1, R, now << 1 | 1); 61 } 62 int calc(Vec v, int now) 63 { 64 if(t[now].l == t[now].r) return v < t[now].vec; 65 if(v < t[now << 1].vec) return calc(v, now << 1) + t[now].sum - t[now << 1].sum; 66 else return calc(v, now << 1 | 1); 67 68 } 69 void update(int idx, int now, int d) 70 { 71 if(t[now].l == t[now].r) 72 { 73 t[now].vec = (Vec){idx, d}; 74 t[now].sum = d ? 1 : 0; //d 75 return; 76 } 77 int mid = (t[now].l + t[now].r) >> 1; 78 if(idx <= mid) update(idx, now << 1, d); 79 else update(idx, now << 1 | 1, d); 80 t[now].vec = max(t[now << 1].vec, t[now << 1 | 1].vec); 81 t[now].sum = t[now << 1].sum + calc(t[now << 1].vec, now << 1 | 1); 82 } 83 84 int main() 85 { 86 n = read(); m = read(); 87 build(1, n, 1); 88 for(int i = 1; i <= m; ++i) 89 { 90 int idx = read(), d = read(); 91 update(idx, 1, d); 92 write(t[1].sum), enter; 93 } 94 return 0; 95 }