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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

[省选联考 2020 A/B 卷] 冰火战士(树状数组上二分)

發布時間:2023/12/3 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [省选联考 2020 A/B 卷] 冰火战士(树状数组上二分) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • problem
  • solution(10pts)
  • code(10pts)
  • solution(30pts)
  • code(30pts)
  • solution(60pts)
  • code(60pts)
  • solution(100pts)
  • code(100pts)

problem

luogu-P6619

一場比賽即將開始。

每位戰士有兩個屬性:溫度和能量。

有兩派戰士:

  • 冰系戰士的技能會對周圍造成降溫冰凍傷害,因而要求場地溫度不低于他的自身溫度才能參賽;
  • 火系戰士的技能會對周圍造成升溫灼燒傷害,因而要求場地溫度不高于他的自身溫度才能參賽。

當場地溫度確定時,雙方能夠參賽的戰士分別排成一隊。

冰系戰士按自身溫度從低到高排序;火系戰士按自身溫度從高到低排序;溫度相同時能量大的戰士排在前面。

首先,雙方的第一位戰士之間展開戰斗,兩位戰士消耗相同的能量,能量少的戰士將耗盡能量退出比賽,而能量有剩余的戰士將繼續和對方的下一位戰士戰斗(能量都耗盡則雙方下一位戰士之間展開戰斗)。

如此循環,直至某方戰士隊列為空,比賽結束。

你需要尋找最佳場地溫度:使冰火雙方消耗總能量最高的溫度的最高值。

現在,比賽還處于報名階段,目前還沒有任何戰士報名,接下來你將不斷地收到報名信息和撤回信息。

其中,報名信息包含報名戰士的派系和兩個屬性,撤回信息包含要撤回的報名信息的序號。

每當報名情況發生變化(即收到一條信息)時,你需要立即報出當前局面下的最佳場地溫度,以及該場地溫度下雙方消耗的總能量之和是多少。

若當前局面下無論何種溫度都無法開展比賽(某一方沒有戰士能參賽),則只要輸出 Peace。

solution(10pts)

  • Q≤100,x≤103Q\le 100,x\le 10^3Q100,x103

枚舉整個信息讀入過程,O(Q)O(Q)O(Q)

然后枚舉答案溫度,O(T)O(T)O(T)

再計算當前溫度下報名的且可以參賽的冰系戰士/火系戰士的總能量,O(Q)O(Q)O(Q)

取所有溫度下二者的較小值的最大值,輸出其的兩倍及記錄的溫度。

時間復雜度 O(Q2T)O(Q^2T)O(Q2T) ,其中 TTT 是所有戰士的溫度最大值。

可以獲得 10′10'10 的普通成績。

code(10pts)

namespace subtask1 {bool vis[105];void solve() {for( int i = 1, op, k, x, y;i <= Q;i ++ ) {scanf( "%d %d", &op, &k );if( op & 1 ) {scanf( "%d %d", &x, &y );g[i] = { k, x, y };vis[i] = 1;}else vis[k] = -1;int ans = 0, ti;for( int T = 1;T <= 1e3;T ++ ) {int ice = 0, fire = 0;for( int j = 1;j <= i;j ++ )if( vis[j] == 1 ) {if( g[j].k == 0 and g[j].x <= T ) ice += g[j].y;if( g[j].k == 1 and g[j].x >= T ) fire += g[j].y;}if( ans < min( ice, fire ) ) ans = min( ice, fire ), ti = T;else if( ans == min( ice, fire ) ) ti = max( ti, T );}if( ! ans ) puts( "Peace" );else printf( "%d %d\n", ti, ans << 1 );}} }

solution(30pts)

  • Q≤104,x≤5000Q\le 10^4,x\le 5000Q104,x5000,不存在撤回信息,且輸入的 xxx 按順序不降。

每次枚舉溫度再枚舉戰士計算的原始暴力就不可行了。

關注特殊的兩個性質:

  • 不存在撤回信息。

    我們原始暴力為什么要枚舉戰士,不僅僅是為了計算戰士的能量、溫度,更重要的是我們不知道當時某個戰士是否已經撤回了報名信息。

    如果不撤回,我們就期望能找到一個數據結構或者什么工具來維護這些戰士的能量和。

  • 輸入的 xxx 按順序不降。

    已知冰系戰士的溫度從小到大排序,火系戰士的溫度從大到小排序。

    所以每次加入戰士,如果是冰系就排在冰系隊伍的最后面;如果是火系就排在火系隊伍的最前面。

    我們可以用兩個數組來模擬這個過程,用指針移動即可。

    且當兩個戰士同派系且溫度一樣的話,就沒有什么區別了,所以直接合并成一個戰士。

最后考慮計算答案。

從高到低枚舉參與比賽的溫度最低的火系戰士,即當時的場地溫度。(證明見滿分題解)

則冰系戰士能參與比賽的一定對應的是其隊伍的一段前綴。

而隨著枚舉的火系戰士溫度降低,冰系戰士能參加比賽的數量也在減少。

所有可以用指針枚舉能參與比賽的溫度最高的冰系戰士。

時間復雜度 O(Q2)O(Q^2)O(Q2)。結合前一檔,可以獲得 30′30'30 的不錯成績。

code(30pts)

namespace subtask1 {} namespace subtask2 {pair < int, int > ice[10005], fire[10005];void solve() {int p0 = 0, p1 = Q + 1, sum_ice = 0;for( int i = 1, op, k, x, y;i <= Q;i ++ ) {scanf( "%d %d %d %d", &op, &k, &x, &y );if( k == 0 ) {sum_ice += y;if( p0 and ice[p0].first == x ) ice[p0].second += y;else ice[++ p0] = make_pair( x, y );}else {if( p1 <= Q and fire[p1].first == x ) fire[p1].second += y;else fire[-- p1] = make_pair( x, y );}int p = p0, Sice = sum_ice, Sfire = 0, ans = 0, ti;for( int j = p1;j <= Q;j ++ ) {int T = fire[j].first; Sfire += fire[j].second;while( T < ice[p].first ) Sice -= ice[p --].second;if( ans < min( Sice, Sfire ) ) ans = min( Sice, Sfire ), ti = T;if( ans == min( Sice, Sfire ) ) ti = max( ti, T );}if( ! ans ) puts("Peace");else printf( "%d %d\n", ti, ans << 1 );}} }

solution(60pts)

  • Q≤2e5,x≤2e5Q\le 2e5,x\le 2e5Q2e5,x2e5

再次注意到,冰系戰士是按溫度從低到高,火系戰士是按溫度從高到低。

即冰系戰士不降,火系戰士不升。

而冰系戰士參加比賽人數越多(前綴取得越多),火系戰士就越少;反之火系越多,冰系越少。

所以我們大膽猜測可以三分?快速求前綴能量和,利用樹狀數組/線段樹均可。

實測寫了后,很遺憾,消耗總能量最高都求不對。輸出中間三分點,發現存在平臺。

仔細想想,確實很容易就出現平臺,因為戰士的溫度是離散的。

平臺三分不行。我們考慮二分。

如果火系戰士的總能量更大,就再升高一下場次溫度;反之降溫。(這其實是正解的做差后的二分思想了)

寫了后,又哭又笑,消耗總能量最高求對了。但是場次溫度并不是最大值。

同樣是因為溫度的離散問題。

但是我們肯定溫度一定是某個戰士的自身溫度。

所以直接暴力一點,去找比記錄溫度高的戰士溫度,然后計算答案是否和總能量一樣,一樣就記錄這個溫度為場地溫度,繼續找;知道總能量消耗小于答案為止。

所以還要用個 set\text{set}set 來記錄所有報名的戰士的溫度。

時間復雜度 O(Qlog?2T)O(Q\log^2 T)O(Qlog2T)(因為那個暴力也跳不了多少次,復雜度前面最多乘個幾的常數)。

結合前兩檔的部分,可以獲得 60′60'60 的優秀成績。

code(60pts)

namespace subtask1 {} namespace subtask2 {} namespace subtask3 {#define maxn 200005#define lson now << 1#define rson now << 1 | 1#define mid (l + r >> 1)set < pair < int, int > > val;namespace ice {int sum[maxn << 2];void modify( int p, int x, int now = 1, int l = 1, int r = 2e5 ) {if( l == r ) { sum[now] += x; return; }if( p <= mid ) modify( p, x, lson, l, mid );else modify( p, x, rson, mid + 1, r );sum[now] = sum[lson] + sum[rson];}int query( int L, int R, int now = 1, int l = 1, int r = 2e5 ) {if( R < l or r < L ) return 0;if( L <= l and r <= R ) return sum[now];return query( L, R, lson, l, mid ) + query( L, R, rson, mid + 1, r );}}namespace fire {int sum[maxn << 2];void modify( int p, int x, int now = 1, int l = 1, int r = 2e5 ) {if( l == r ) { sum[now] += x; return; }if( p <= mid ) modify( p, x, lson, l, mid );else modify( p, x, rson, mid + 1, r );sum[now] = sum[lson] + sum[rson];}int query( int L, int R, int now = 1, int l = 1, int r = 2e5 ) {if( R < l or r < L ) return 0;if( L <= l and r <= R ) return sum[now];return query( L, R, lson, l, mid ) + query( L, R, rson, mid + 1, r );}}int find( int x, int ans ) {while( 1 ) {auto it = val.upper_bound( make_pair( x, Q ) );if( it == val.end() ) return x;int sum1 = fire :: query( it->first, 2e5 );int sum2 = ice :: query( 1, it->first );if( ans > min( sum1, sum2 ) ) return x;else x = it->first;}}void solve() {int Min = 1e9, Max = -1e9;for( int i = 1, op, k, x, y;i <= Q;i ++ ) {scanf( "%d %d", &op, &k );if( op & 1 ) {scanf( "%d %d", &x, &y );val.insert( make_pair( x, i ) );Min = min( Min, x );Max = max( Max, x );g[i] = { k, x, y };if( k == 0 ) ice :: modify( x, y );else fire :: modify( x, y );}else {val.erase( make_pair( g[k].x, k ) );if( g[k].k == 0 ) ice :: modify( g[k].x, - g[k].y );else fire :: modify( g[k].x, - g[k].y );}int l = 1, r = 2e5;int ans = 0, ti;while( l <= r ) {int sum1 = fire :: query( mid, 2e5 );int sum2 = ice :: query( 1, mid );if( ans < min( sum1, sum2 ) ) ans = min( sum1, sum2 ), ti = mid;else if( ans == min( sum1, sum2 ) ) ti = mid;if( sum1 >= sum2 ) l = mid + 1;else r = mid - 1;}if( ! ans ) puts("Peace");else printf( "%d %d\n", find( ti, ans ), ans << 1 );}} }

solution(100pts)

  • Q≤2e6,1≤x≤2e9Q\le 2e6,1\le x\le 2e9Q2e6,1x2e9

observation1:\text{observation1}:observation1: 冰系戰士排列后溫度成不降函數;火系戰士排列后溫度成不升函數。

observation2:\text{observation2}:observation2: 最后的場地溫度一定是某個戰士的體溫。

  • 證明:顯然。感性感知一下。

    如果最后的場地溫度不是某個戰士的溫度。

    那么這個場地溫度一定 <<< 參與比賽的火系戰士的最低溫度,>>> 參與比賽的冰系戰士的最高溫度。

    這個場地溫度就可以被調整為 === 參與比賽的火系戰士的最低溫度。

    這樣火系和冰系戰士原本能參與比賽的沒有變化,說不定還可以新增幾名參與比賽的冰系戰士。

    溫度拉高,總消耗也沒有變劣。

基于這個結論,我們可以將戰士的溫度離散化成和 QQQ 同量級。

假設場地溫度為 TTT,能參加比賽的冰系戰士的能量和為 I(T)\text{I}(T)I(T),能參加比賽的火系戰士的能量和為 F(T)\text{F}(T)F(T)

勾勒成函數后,一個單增(火),一個單減(冰)。答案肯定是在交點處。

而我們真正要二分的應該兩函數合并取較小值的函數。

更詳細地,我們記 Ice(i):\text{Ice}(i):Ice(i): 自身溫度 ≤i\le ii 的冰系戰士的能量和,記 Fire(i):\text{Fire}(i):Fire(i): 自身溫度 ≤i\le ii 的火系戰士的能量和,記當前報名的所有火系戰士的能量和為 sumsumsum

則當場地溫度為 TTT 的時候,答案即為 min?(Ice(T),sum?Fire(T?1))\min\Big(\text{Ice}(T),sum-\text{Fire}(T-1)\Big)min(Ice(T),sum?Fire(T?1)) 再乘 222

我們二分的函數應該是兩個前綴和函數做差:G(T)=Ice(T)?(sum?Fire(T?1))G(T)=\text{Ice}(T)-(sum-\text{Fire}(T-1))G(T)=Ice(T)?(sum?Fire(T?1))。(與上圖不太一樣,反轉了火系函數)

因為 Ice(T)\text{Ice}(T)Ice(T) 函數不降,sum?(Fire(T?1))sum-(\text{Fire}(T-1))sum?(Fire(T?1)) 函數不升,所以 G(T)G(T)G(T) 函數非嚴格不降。

那么交點必定在最后一個 ≤0\le 00 和第一個 >0>0>0 的位置之間。

所以我們二分最后一個使得 G(T)≤0G(T)\le 0G(T)0 的位置 ttt,答案必定在 t/t+1t/t+1t/t+1 處取得。

如果在 ttt 處答案更大,則溫度自然就是 ttt

否則還要在函數平臺處二分出最大的溫度 t0t_0t0?,滿足 Ice(t+1)?(sum?Fire(t))=Ice(t0)?(sum?Fire(t0?1))?Fire(t)≥Fire(t0?1)\text{Ice}(t+1)-(sum-\text{Fire}(t))=\text{Ice}(t_0)-(sum-\text{Fire}(t_0-1))\Rightarrow \text{Fire}(t)\ge \text{Fire}(t_0-1)Ice(t+1)?(sum?Fire(t))=Ice(t0?)?(sum?Fire(t0??1))?Fire(t)Fire(t0??1)

但肯定不能二分后樹狀數組查詢,這樣跟 60′60'60 的做法是一樣的。(推導過程嚴謹了一些而已,但誰管這個呢?》》

這里我們要用到 樹狀數組上二分,與線段樹上二分類似,是 O(nlog?n)O(n\log n)O(nlogn) 的。

為什么不用線段樹二分,是因為這位雖然看似一個 log?\loglog 但是跑出來因為常數問題,跟二分后樹狀數組差不多。

  • 樹狀數組二分。

這需要改寫一下上面函數的形式:

fire(i):fire(i):fire(i): 溫度為 iii 的火系戰士的能量和。

  • Ice(T)?(sum?Fire(T?1))≤0?Ice(T)+Fire(T)?fire(T)≤sum\text{Ice}(T)-(sum-\text{Fire}(T-1))\le 0\Rightarrow \text{Ice(T)}+\text{Fire}(T)-fire(T)\le sumIce(T)?(sum?Fire(T?1))0?Ice(T)+Fire(T)?fire(T)sum

    這樣下標只有 TTT 且具有單調性,可以樹狀數組二分了。

  • Fire(T)?fire(T)≤sum?ans\text{Fire}(T)-fire(T)\le sum-ansFire(T)?fire(T)sum?ans

時間復雜度 O(Qlog?Q)O(Q\log Q)O(QlogQ),可以獲得 100′100'100 的好成績。

code(100pts)

#include <bits/stdc++.h> using namespace std; #define maxn 2000005 int val[maxn], fire[maxn]; int Q, n, cnt_fire, sum_fire, cnt_ice; struct node { int op, k, x, y; }q[maxn]; struct BIT {int sum[maxn];void modify( int i, int x ) {for( ;i <= n;i += i & -i ) sum[i] += x;}int query( int i ) {int ans = 0;for( ;i;i -= i & -i ) ans += sum[i];return ans;} }Fire, Ice;int calc( int x ) {return min( Ice.query( x ), sum_fire - Fire.query( x - 1 ) ); }int main() {scanf( "%d", &Q );for( int i = 1;i <= Q;i ++ ) {scanf( "%d %d", &q[i].op, &q[i].k );if( q[i].op & 1 ) scanf( "%d %d", &q[i].x, &q[i].y ), val[++ n] = q[i].x;}sort( val + 1, val + n + 1 );n = unique( val + 1, val + n + 1 ) - val - 1;for( int i = 1;i <= Q;i ++ )if( q[i].op & 1 ) q[i].x = lower_bound( val + 1, val + n + 1, q[i].x ) - val;for( int t = 1;t <= Q;t ++ ) {int k = q[t].k, x, y;if( q[t].op & 1 ) {x = q[t].x, y = q[t].y;if( k == 0 ) Ice.modify( x, y ), cnt_ice ++;else Fire.modify( x, y ), cnt_fire ++, sum_fire += y, fire[x] += y; }else {x = q[k].x, y = q[k].y;if( q[k].k == 0 ) Ice.modify( x, -y ), cnt_ice --;else Fire.modify( x, -y ), cnt_fire --, sum_fire -= y, fire[x] -= y;}int p = 0, ans = 0;for( int i = 20;~ i;i -- ) {k = p | (1 << i);if( k <= n and ans + Ice.sum[k] + Fire.sum[k] - fire[k] <= sum_fire )p |= (1 << i), ans += Ice.sum[k] + Fire.sum[k];}int ans1 = calc( p ), ans2 = calc( p + 1 );if( max( ans1, ans2 ) <= 0 ) { puts("Peace"); continue; }if( ans1 > ans2 ) { printf( "%d %d\n", val[p], ans1 << 1 ); continue; }p = 0, ans = 0;for( int i = 20;~ i;i -- ) {k = p | (1 << i);if( k <= n and ans + Fire.sum[k] - fire[k] <= sum_fire - ans2 )p |= (1 << i), ans += Fire.sum[k];/*ans2說明火系戰士的能量和更小二分平臺上的最大溫度t0在前綴和Fire數組中以T為自變量呈一個平臺滿足 Fire(t)>=Fire(t0-1)即 sum-Fire(t)<=sum-Fire(t0-1)*/}printf( "%d %d\n", val[p], ans2 << 1 );}return 0; }

總結

以上是生活随笔為你收集整理的[省选联考 2020 A/B 卷] 冰火战士(树状数组上二分)的全部內容,希望文章能夠幫你解決所遇到的問題。

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