日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[luogu P4198] 楼房重建(线段树 + 思维)

發布時間:2023/12/3 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [luogu P4198] 楼房重建(线段树 + 思维) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

luogu 樓房重建

  • problem
  • solution
  • code

problem

洛谷鏈接

solution

非常巧妙的一道題,對線段樹的運用很靈活。

顯然這個與原點的連線可以想到將每個點轉化為與原點連線形成的直線斜率。

答案其實就是:從第一個點開始選,后一個斜率比前面大的必須選,否則,即小于等于則必須不選的選擇個數。

區間是固定的,且左邊的一些信息影響右邊,每次還要單點修改,可以聯想到線段樹。

這個線段樹沒有懶標記,自然也沒有下放標記操作。唯一的難點就是在于合并操作的實現。

線段樹區間 [l,r][l,r][l,r] 維護從 lll 開始選,按照答案的規則選出的個數答案。

最終輸出線段樹節點 111 表示 [1,n][1,n][1,n] 區間的答案即可。

顯然,一段區間的第一個一定被選,一段區間中的最大值也一定被選。

而一段區間的最大值會影響后面區間的可選值,所以還要維護一個區間 [l,r][l,r][l,r] 中最大的斜率。

考慮左右兒子具體怎么合并給父親。

首先,左兒子合并上去后做的是開頭部分,所以左兒子的答案一定貢獻。

其次,左兒子一定會選其區間內的斜率最大值,而右兒子的斜率都必須大于這個最大值。

這里,我們采取遞歸實現合并方式,pushup(now,l,r,k) 表示要求線段樹 nownownow 節點代表的區間 [l,r][l,r][l,r] 選擇的斜率必須大于 kkk 的答案。

一開始是要求右兒子選擇整體要大于左兒子的斜率最大值的。

t[now].ans = t[lson].ans + pushup( rson, mid + 1, r, t[lson].Max );

然后開始遞歸考慮右兒子的子樹,即具體的 pushup 實現。

  • 如果節點內的最大值都不超過要求的斜率,直接返回。

    if( t[now].Max <= k ) return 0;
  • 如果左區間的最大值都不超過要求的斜率,那么直接找右區間即可。

    if( t[lson].Max <= k ) return pushup( rson, mid + 1, r, k );
  • 否則,就要去找左區間的答案。左區間既然有些要被選,那么就沒有影響右區間的開始,直接統計右區間的答案。

    而右區間的答案不是 t[rson].ans 而是 t[now].ans-t[lson].ans。

    因為右區間可能有些會被左區間擋住不能選,就只能換一種方法表示了。

    return pushup( lson, l, mid, k ) + t[now].ans - t[lson].ans;
  • 走到葉子節點,就直接判斷是否大于要求的斜率即可。

    if( l == r ) return t[now].Max > k;
  • trick

    可以記錄一下橫坐標為 lll 的點與原點形成直線的斜率。

    如果這個斜率就一定大于要求值的話,那么就相當于從 lll 開始按照規則選的個數。

    即線段樹上 [l,r][l,r][l,r] 區間維護的信息,那么直接返回就不用再往下操作了。

    if( g[l] > k ) return t[now].ans;

code

#include <bits/stdc++.h> using namespace std; #define maxn 100005 struct node { double Max; int ans; }t[maxn << 2]; double g[maxn];#define lson now << 1 #define rson now << 1 | 1 #define mid ( ( l + r ) >> 1 )int pushup( int now, int l, int r, double k ) {if( t[now].Max <= k ) return 0;if( k < g[l] ) return t[now].ans;if( l == r ) return t[now].Max > k;if( t[lson].Max <= k ) return pushup( rson, mid + 1, r, k );else return pushup( lson, l, mid, k ) + t[now].ans - t[lson].ans; }void modify( int now, int l, int r, int x, int y ) {if( l == r ) { t[now].Max = 1.0 * y / x, t[now].ans = 1; return; }if( x <= mid ) modify( lson, l, mid, x, y );else modify( rson, mid + 1, r, x, y );t[now].Max = max( t[lson].Max, t[rson].Max );t[now].ans = t[lson].ans + pushup( rson, mid + 1, r, t[lson].Max ); }int main() {int n, m, x, y;scanf( "%d %d", &n, &m );while( m -- ) {scanf( "%d %d", &x, &y );g[x] = 1.0 * y / x;modify( 1, 1, n, x, y );printf( "%d\n", t[1].ans );} }

總結

以上是生活随笔為你收集整理的[luogu P4198] 楼房重建(线段树 + 思维)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。