数据结构之线段树Ⅴ——(李超线段树)Robot,Product Sum,Building Bridges,Jump mission
文章目錄
- Robot
- Product Sum
- Building Bridges
- Jump mission
Robot
BZOJ3938
機器人每次一旦改變速度,直到下一次改變速度為止
這一時間段內機器人的位置下標可以用一次函數表示
如果知道時刻t1t_1t1?即將改變速度的機器人位置,以及最近的下一次機器人速度再次變化的時刻t2t_2t2?
利用數學工具,可以算出這條線段的解析式(兩點式計算)
(t1,d)?(t2,d+v(t2?t1))\big(t_1,d\big)-\big(t_2,d+v(t_2-t_1)\big)(t1?,d)?(t2?,d+v(t2??t1?))
機器人的多次更換速度,那么一個機器人的位置其實就是若干條折線
將操作按照時間排序,記錄上一次同一個機器人的操作時間,速度等然后計算出線段
李超數就可以查詢ttt時刻的最遠值了(位置有正負所以計算后要取絕對值哦)
還有時間ttt的離散化,線段樹內存不允許開[0,1e9][0,1e9][0,1e9]這么大
#include <cstdio> #include <iostream> #include <algorithm> using namespace std; #define int long long #define inf 1e9 #define maxn 600005 #define lson num << 1 #define rson num << 1 | 1 struct node{int k, b;node(){}node( int K, int B ) { k = K, b = B; } }t[maxn << 2];struct query { int ti, k, x, opt; }q[maxn];int d[maxn], Time[maxn], V[maxn], lst_t[maxn], lst_v[maxn], T[maxn], val[maxn]; bool vis[maxn]; int m;void build( int num, int l, int r ) {t[num] = node( 0, 0 );if( l == r ) return;int mid = l + r >> 1;build( lson, l, mid );build( rson, mid + 1, r ); }int Fabs( int x ) { return x < 0 ? -x : x; }int calc( node l, int x ) { return Fabs( l.k * T[x] + l.b ); }bool cover( node lst, node now, int x ) { return calc( lst, x ) < calc( now, x ); }void insert( int num, int l, int r, node now ) {if( cover( t[num], now, l ) and cover( t[num], now, r ) ) {t[num] = now;return;}if( l == r ) return;int mid = l + r >> 1;if( cover( t[num], now, mid ) ) swap( t[num], now );if( cover( t[num], now, l ) ) insert( lson, l, mid, now );if( cover( t[num], now, r ) ) insert( rson, mid + 1, r, now ); }void modify( int num, int l, int r, int L, int R, node now ) {if( R < l or r < L ) return;if( L <= l and r <= R ) { insert( num, l, r, now ); return; }int mid = l + r >> 1;modify( lson, l, mid, L, R, now );modify( rson, mid + 1, r, L, R, now ); }int query( int num, int l, int r, int x ) {if( l == r ) return calc( t[num], x );int mid = l + r >> 1, ans;if( x <= mid ) ans = query( lson, l, mid, x );else ans = query( rson, mid + 1, r, x );return max( ans, calc( t[num], x ) ); }int find( int x ) { return lower_bound( T + 1, T + m + 1, x ) - T; }signed main() {int n, Q; char opt[10];scanf( "%lld %lld", &n, &Q ); build( 1, 0, maxn );for( int i = 1;i <= n;i ++ ) {scanf( "%lld", &d[i] );modify( 1, 0, maxn, 0, 0, node( 0, d[i] ) );}T[++ m] = 0;for( int i = 1;i <= Q;i ++ ) {scanf( "%lld %s", &q[i].ti, opt );T[++ m] = q[i].ti;if( opt[0] == 'c' ) {q[i].opt = 0;scanf( "%lld %lld", &q[i].k, &q[i].x );lst_t[i] = Time[q[i].k], Time[q[i].k] = q[i].ti;lst_v[i] = V[q[i].k], V[q[i].k] = q[i].x;}else q[i].opt = 1;}sort( T + 1, T + m + 1 );m = unique( T + 1, T + m + 1 ) - T - 1;for( int i = 1;i <= Q;i ++ )if( q[i].opt ) continue;else {int dt = q[i].ti - lst_t[i];int y = dt * lst_v[i] + d[q[i].k];int k = lst_v[i];int b = y - q[i].ti * k;modify( 1, 0, m, find( lst_t[i] ), find( q[i].ti ), node( k, b ) );d[q[i].k] = y;}for( int i = Q;i;i -- )if( vis[q[i].k] ) continue;else {vis[q[i].k] = 1;int dt = inf - q[i].ti;int y = dt * q[i].x + d[q[i].k];int k = q[i].x;int b = y - inf * k;modify( 1, 0, m, find( q[i].ti ), m, node( k, b ) );}for( int i = 1;i <= n;i ++ )if( ! vis[i] ) modify( 1, 0, m, 0, m, node( 0, d[i] ) );for( int i = 1;i <= Q;i ++ )if( q[i].opt ) printf( "%lld\n", query( 1, 0, m, find( q[i].ti ) ) );return 0; }Product Sum
CF631E
令S=∑i=1ni?aiS=\sum_{i=1}^ni*a_iS=∑i=1n?i?ai?
考慮當位置iii的數插到位置jjj的時,權值的變化
-
j<ij<ij<i,位置iii的數往前插
-
?k∈[1,j)?(i,n]k?ak\forall k\in[1,j)\bigcup (i,n]\quad k*a_k?k∈[1,j)?(i,n]k?ak?沒有變化
-
?k∈[j,i)\forall k\in[j,i)?k∈[j,i) 每個數都會往后移一位,k?ak←(k+1)?akk*a_k\leftarrow (k+1)*a_kk?ak?←(k+1)?ak?,SSS增加∑k=ji?1ak\sum_{k=j}^{i-1}a_k∑k=ji?1?ak?
-
iii前移i?ji-ji?j位到jjj,i?ai←j?aii*a_i\leftarrow j*a_ii?ai?←j?ai?,SSS減少(i?j)ai(i-j)a_i(i?j)ai?
-
設fif_ifi? :考慮第iii個位置往前移動到某個位置時的最大權值,sumi=∑j=1iajsum_i=\sum_{j=1}^ia_jsumi?=∑j=1i?aj?
-
從小到大枚舉i
-
fi=min?{S+sumi?1?sumj?1+(i?j)ai}=S+sumi?1+i?ai+min?{?j?ai?sumj?1}f_i=\min\Big\{S+sum_{i-1}-sum_{j-1}+(i-j)a_i\Big\}=S+sum_{i-1}+i·a_i+\min\Big\{-j*a_i-sum_{j-1}\Big\} fi?=min{S+sumi?1??sumj?1?+(i?j)ai?}=S+sumi?1?+i?ai?+min{?j?ai??sumj?1?}
-
?j-j?j看做直線斜率kkk,?sumj?1-sum_{j-1}?sumj?1?看做直線截距bbb,aia_iai?就是因變量xxx
-
-
j>ij>ij>i,位置iii的數往后插
-
?k∈[1,i)?(j,n]k?ak\forall k\in[1,i)\bigcup (j,n]\quad k*a_k?k∈[1,i)?(j,n]k?ak?沒有變化
-
?k∈(i,j]\forall k\in(i,j]?k∈(i,j] 每個數都會往前移一位,k?ak←(k?1)?akk*a_k\leftarrow (k-1)*a_kk?ak?←(k?1)?ak?,SSS減少∑k=i+1jak\sum_{k=i+1}^ja_k∑k=i+1j?ak?
-
iii后移j?ij-ij?i位到jjj,i?ai←j?aii*a_i\leftarrow j*a_ii?ai?←j?ai?,SSS增加(j?i)ai(j-i)a_i(j?i)ai?
-
設fif_ifi? :考慮第iii個位置往移動后到某個位置時的最大權值,sumi=∑j=1iajsum_i=\sum_{j=1}^ia_jsumi?=∑j=1i?aj?
-
從大到小枚舉i
-
fi=min?{S?(sumj?sumi)+(j?i)ai}=S+sumi?i?ai+min?{j?ai?sumj}f_i=\min\Big\{S-(sum_j-sum_i)+(j-i)a_i\Big\}=S+sum_i-i·a_i+\min\Big\{j*a_i-sum_j\Big\} fi?=min{S?(sumj??sumi?)+(j?i)ai?}=S+sumi??i?ai?+min{j?ai??sumj?}
-
jjj看做直線斜率kkk,?sumj-sum_{j}?sumj?看做直線截距bbb,aia_iai?就是因變量xxx
-
Building Bridges
LOJ2483
設dpi:dp_i:dpi?: 使1?i1-i1?i聯通的最小花費
考慮暴力dpdpdp轉移,枚舉橋建筑在jjj和iii根柱子之間
dpi=min?{dpj+∑k=j+1i?1wk+(hi?hj)2}dp_i=\min\Big\{dp_j+\sum_{k=j+1}^{i-1}w_k+(h_i-h_j)^2\Big\}dpi?=min{dpj?+∑k=j+1i?1?wk?+(hi??hj?)2}
前綴和優化,sumi=∑j=1iwjsum_i=\sum_{j=1}^iw_jsumi?=∑j=1i?wj?
dpi=min?{dpj+sumi?1?sumj+hi2?2hihj+hj2}=sumi?1+hi2+min?{?2hj?hi+dpj?sumj+hj2}dp_i=\min\Big\{dp_j+sum_{i-1}-sum_{j}+h_i^2-2h_ih_j+h_j^2\Big\}\\ =sum_{i-1}+h_i^2+\min\Big\{-2h_j*h_i+dp_j-sum_j+h_j^2\Big\} dpi?=min{dpj?+sumi?1??sumj?+hi2??2hi?hj?+hj2?}=sumi?1?+hi2?+min{?2hj??hi?+dpj??sumj?+hj2?}
Jump mission
CodeChef
設dpi:dp_i:dpi?: 在iii座山停留的最小花費
考慮暴力dpdpdp轉移,枚舉上一次停留在jjj山
dpi=min?{dpj+Ai+hi2?2hihj+hj2}=Ai+hi2+min?{?2hj?hi+dpj+hj2}dp_i=\min\Big\{dp_j+A_i+h_i^2-2h_ih_j+h_j^2\Big\}=A_i+h_i^2+\min\Big\{-2h_j*h_i+dp_j+h_j^2\Big\} dpi?=min{dpj?+Ai?+hi2??2hi?hj?+hj2?}=Ai?+hi2?+min{?2hj??hi?+dpj?+hj2?}
從小到大枚舉iii,利用前面枚舉過的山當jjj,這只能保證j<ij<ij<i的條件
然而本題多了一個Pj<PiP_j<P_iPj?<Pi?的限制
這個時候只有獻出神祭——樹套樹,將PPP利用樹狀數組嵌套在李超線段樹外面
自然內存就會多一些,看著開就行了
#include <cstdio> #include <iostream> using namespace std; #define int long long #define inf 5e18 #define maxn 600000 struct node {int k, b;node(){ k = 0, b = inf; }node( int K, int B ) { k = K, b = B; } }t[maxn * 80]; int lson[maxn * 80], rson[maxn * 80]; int cnt;int calc( node l, int x ) { return l.k * x + l.b; }bool cover( node lst, node now, int x ) { return calc( lst, x ) > calc( now, x ); }void insert( int &num, int l, int r, node now ) {if( ! num ) num = ++ cnt, t[num] = node( 0, inf );if( cover( t[num], now, l ) and cover( t[num], now, r ) ) {t[num] = now;return;}if( l == r ) return;int mid = ( l + r ) >> 1;if( cover( t[num], now, mid ) ) swap( t[num], now );if( cover( t[num], now, l ) ) insert( lson[num], l, mid, now );if( cover( t[num], now, r ) ) insert( rson[num], mid + 1, r, now ); }int query( int num, int l, int r, int x ) {if( ! num ) return inf;if( l == r ) return calc( t[num], x );int mid = ( l + r ) >> 1, ans;if( x <= mid ) ans = query( lson[num], l, mid, x );else ans = query( rson[num], mid + 1, r, x );return min( ans, calc( t[num], x ) ); }int p[maxn], a[maxn], h[maxn], dp[maxn], root[maxn]; int n;int lowbit( int i ) { return i & -i; }void add( int i, node l ) {for( ;i <= n;i += lowbit( i ) ) insert( root[i], 1, maxn, l ); }int query( int i, int x ) {int ans = inf;for( ;i;i -= lowbit( i ) ) ans = min( ans, query( root[i], 1, maxn, x ) );return ans; }signed main() {scanf( "%lld", &n );for( int i = 1;i <= n;i ++ ) scanf( "%lld", &p[i] );for( int i = 1;i <= n;i ++ ) scanf( "%lld", &a[i] );for( int i = 1;i <= n;i ++ ) scanf( "%lld", &h[i] );dp[1] = a[1], add( p[1], node( -2 * h[1], h[1] * h[1] + dp[1] ) );for( int i = 2;i <= n;i ++ ) {dp[i] = a[i] + h[i] * h[i] + query( p[i], h[i] );add( p[i], node( - 2 * h[i], h[i] * h[i] + dp[i] ) );}printf( "%lld\n", dp[n] );return 0; }李超數注意兩點即可
- 推出能夠轉化成直線解析式的轉移方程式
- 確定線段樹點的區間范圍,究竟是哪個值做線段樹區間,是否需要離散化
總結
以上是生活随笔為你收集整理的数据结构之线段树Ⅴ——(李超线段树)Robot,Product Sum,Building Bridges,Jump mission的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 林允儿动态壁纸设置方法
- 下一篇: [AtCoder Beginner Co