1. 程式人生 > 實用技巧 >2018ICPC青島I Soldier Game (線段樹)

2018ICPC青島I Soldier Game (線段樹)

題目要求的是分組後的組間最大值-最小值的最小是多少。

因為我們發現有兩個變數,因此很難做,一種經典思路就是固定一個答案,然後求相對於這個答案的另一個值的最優解,列舉所有情況取min即可。

對於本題,我們發現所有的值其實已經明瞭,因此先預處理計算後,我們可以列舉最小值,之後找到滿足條件的最小的最大值。

所謂滿足條件就是找到一種不相互覆蓋的方式能夠覆蓋整個1-n區間

區間覆蓋問題可以使用線段樹來維護,但是這裡要做一點特殊的處理,因為我們有長度為1的邊和長度為2的邊。因此我們維護的資訊是tr[u][2][2],分別表示這個l,r l+1,r l,r+1 l+1,r+1

在pushup的時候用不重複的更新方式去更新,就能得到所有對於能夠覆蓋區間的方案。

現在有一點問題是,對於我的做法如何能保證取到的最小值和最大值一定能組成合法方案呢?

答案是不一定能組成合法方案,但是組不成的不會是最後的答案,所有統統更新即可。

對於細節上,我們可以簡單討論證明。

合法方案不必多說,直接去min

對於其中可能存在的非法方案,也就是你當前最小值點,可以被其他區間替換,並且他們也能做到覆蓋整個區間,那麼當前最小值點是非法的。

例如 2 4 5 7,那麼我們發現去掉這個最小值,我們仍然得到一個更優的並且能滿足題意要求的答案,因此當前答案不會成為最後答案,所以直接更新即可

#include<bits/stdc++.h>
using namespace std;
typedef 
long long ll; typedef pair<int,int> pll; const int N=2e5+10; pll p[N<<2]; int a[N][2]; int tr[N<<2][2][2]; int n; bool cmp(pll x,pll y){ return a[x.first][x.second]<a[y.first][y.second]; } void build(int u,int l,int r){ tr[u][0][0]=tr[u][1][0]=tr[u][1][1]=tr[u][0][1]; if(l==r){ tr[u][
1][0]=1; return ; } int mid=l+r>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); } void pushup(int rt){ tr[rt][0][0] = (tr[rt<<1][0][0] && tr[rt<<1|1][0][0]) || (tr[rt<<1][0][1] && tr[rt<<1|1][1][0]); tr[rt][0][1] = (tr[rt<<1][0][0] && tr[rt<<1|1][0][1]) || (tr[rt<<1][0][1] && tr[rt<<1|1][1][1]); tr[rt][1][0] = (tr[rt<<1][1][0] && tr[rt<<1|1][0][0]) || (tr[rt<<1][1][1] && tr[rt<<1|1][1][0]); tr[rt][1][1] = (tr[rt<<1][1][1] && tr[rt<<1|1][1][1]) || (tr[rt<<1][1][0] && tr[rt<<1|1][0][1]); } void modify(int u,int l,int x,int L,int R){ if(L==R){ tr[u][0][x]^=1; return ; } int mid=L+R>>1; if(l<=mid) modify(u<<1,l,x,L,mid); else modify(u<<1|1,l,x,mid+1,R); pushup(u); } int main(){ ios::sync_with_stdio(false); int t; cin>>t; while(t--){ int cnt=0; cin>>n; int i; for(i=1;i<=n;i++){ cin>>a[i][0]; p[++cnt]={i,0}; if(i-1){ a[i-1][1]=a[i-1][0]+a[i][0]; p[++cnt]={i-1,1}; } } sort(p+1,p+cnt+1,cmp); build(1,1,n); int j; ll ans=1e18; for(i=1,j=1;i<=cnt;i++){ while(j<=cnt&&!tr[1][0][0]){ modify(1,p[j].first,p[j].second,1,n); j++; } if(tr[1][0][0]){ ans=min(ans,1ll*a[p[j-1].first][p[j-1].second]-1ll*a[p[i].first][p[i].second]); } modify(1,p[i].first,p[i].second,1,n); } cout<<ans<<endl; } return 0; }
View Code