1. 程式人生 > 實用技巧 >[2020HDU多校第二場][HDU 6770][H. Dynamic Convex Hull]

[2020HDU多校第二場][HDU 6770][H. Dynamic Convex Hull]

賽後3min 1A...自閉_(:з」∠)_

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=6770

題目大意:維護一個由函式\(f_i(x)=(x-a_i)^4+b_i\)組成的集合,要求實現插入、刪除、以及查詢\(x\):求\(f_i(x)\)的最小值

題解:觀察題中給出函式的性質,考慮兩個函式\(f_i\)與\(f_j\)在何時會出現大小關係變換的情況。根據初中數學知識,\(f_i(x)\)是由函式\(f(x)=x^4\)平移得到的(左加右減,上加下減),因此若令\(g(x)=f_i(x)-f_j(x)\),很明顯能將\(g(x)\)轉換成\((x-a)^4+b-x^4\)的形式(考慮將\(f_i\)與\(f_j\)在座標軸上的影象同時平移,使\(f_j\)與\(f(x)=x^4\)的影象重合),將其展開可以得出\(g(x)=-4ax^3+6a^2x^2-4a^3x+a^4+b\),在\(a \neq 0\)(即\(a_i \neq a_j\))時是一個三次函式。對這個三次函式進行求導可以發現\(g'(x)=-12ax^2+12a^2x-4a^3=-4a(3x^2-3ax+a^2)\),而\(3x^2-3ax+a^2\)這個二次函式的判別式算出來的結果是\(-3a^2<0\),因此\(3x^2-3ax+a^2\)恆大於零,故\(g'(x)<0\)恆成立,\(g(x)\)單調下降。於是對於任意\(a_i\)不同的兩個函式,他們的大小隻會交換一次。另外我們還發現,每次求的是\(f_i(x)\)的最小值,而在\(f_i\)的展開式中,\(x^4\)對函式間的大小毫無影響,所以可以“假裝”將題目轉換成求\(g_i(x)=-4a_ix^3+6a_i^2x^2-4a_i^3x+a_i^4+b_i\)的最小值(這裡指的是用\(g_i\)的性質比大小,求\(f_i\)的值)。而這個函式我們之前驗證過了是嚴格單調下降的,所以題目就變成了求若干個單調下降、且兩兩隻有一個交點的函式的最小值,性質與求\(ax+b\)最小值十分相像。

   另一方面,聯想到CF 678F的做法,考慮每一個函式存在的時間區間,以時間為下標建立線段樹,並在樹上的每個結點記錄下對應區間內的函式,對問題進行離線求解。這樣在建好樹之後,就可以在樹上的每個結點內進行維護當前區間記憶體在的所有函式,由於線段樹的性質,每個函式只會存在於\(O(log n)\)個結點內,因此我們可以考慮如何在\(O(nlog n)\)的時間複雜度內對一個結點上的若干個函式進行求最小值的操作,在總複雜度為\(O(nlog^2n)\)的情況下解決問題。

   由於我們之前發現求\(f_i\)最小值與求\(ax+b\)最小值的性質十分相像,於是考慮也用在每個結點上維護一個類似於下凸殼的東西對此題進行求解。對每個結點內的所有函式按\(a_i\)升序排序(在\(a\)較小時\(g_i\)的下降幅度相對緩慢),並維護一個棧,求出由當前結點內函式組成的“下凸殼”。並記錄棧中每個函式與前一個函式“交點”的位置(第一次交換大小關係的整點位置),由於詢問的\(x\)是整數,因此只需要在整數域內二分進行“求交”即可。最後求答案時,只需對每個結點求一個“下凸殼”,並對該結點對應時間區間中的所有詢問操作進行分別求解。由於我們記錄了棧中相鄰函式“交點”的位置,只需要二分一下就可以求出在當前的“下凸殼”中哪個函式最小並得出對應答案。

   對於每個結點,維護“下凸殼”的操作為\(O(nlog n)\),由於每個函式只會影響到\(O(log n)\)個結點,所以該部分複雜度為\(O(nlog^2n)\)。而對於詢問操作,同樣的有每次詢問只會存在於\(O(nlog n)\)個結點內,因此對於每個詢問也只需要花費\(O(log^2n)\)的時間,故詢問操作的複雜度也為\(O(nlog^2n)\)。總時間複雜度為\(O(nlog^2n)\)。

#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define LL long long
LL T,n,m,cnt,r[N],a[N],b[N],c[N],o[N],q[N],ans[N],fk[N],id[N],C;
struct Point { LL a,b; LL f(LL x){LL tmp=(x-a)*(x-a);return tmp*tmp+b;} bool operator <(const Point&t)const{return a==t.a?b<t.b:a<t.a;} LL isct(Point t){ //a>t.a LL l=1,r=50000,mid; if(f(0)<=t.f(0))return 0; if(f(50000)>t.f(50000))return 50001; while(l<r){ mid=(l+r)>>1; if(f(mid)>t.f(mid))l=mid+1; else r=mid; } return l; } }st[N]; struct Segment_Tree { struct rua{ LL l,r; vector<Point>d; }t[N<<2]; void Build(LL l,LL r,LL x){ t[x].d.clear(); t[x].l=l,t[x].r=r; if(l==r)return; LL mid=(l+r)>>1; Build(l,mid,x*2); Build(mid+1,r,x*2+1); } void change(LL L,LL R,Point p,LL x){ LL l=t[x].l,r=t[x].r; LL mid=(l+r)>>1; if(L<=l && r<=R){t[x].d.push_back(p);return;} if(L<=mid)change(L,R,p,x*2); if(mid<R)change(L,R,p,x*2+1); } void ask(LL i){ LL x=q[i]; LL k=upper_bound(fk+1,fk+cnt+1,x)-fk-1; //cout<<i<<" "<<x<<endl; //cout<<k<<" "<<st[k].a<<" "<<st[k].b<<endl; ans[i]=min(ans[i],st[k].f(x)); } void get(LL x){ if(t[x].l<t[x].r){ get(x*2); get(x*2+1); } cnt=0; sort(t[x].d.begin(),t[x].d.end()); for(auto p:t[x].d){ if(p.a==st[cnt].a)continue; while(cnt>1){ LL tmp=p.isct(st[cnt-1]); if(tmp>fk[cnt])break; if(tmp==fk[cnt] && p.f(fk[cnt])>st[cnt].f(fk[cnt]))break; cnt--; } st[++cnt]=p; if(cnt==1)fk[cnt]=0; else fk[cnt]=st[cnt].isct(st[cnt-1]); } for(LL i=t[x].l;i<=t[x].r;i++) if(o[i]==3 && cnt && c[i])ask(i); } }Tr; void init() { C=cnt=0; scanf("%lld%lld",&n,&m); Tr.Build(1,n+m,1); for(LL i=1;i<=n;i++){ o[i]=1; scanf("%lld%lld",&a[i],&b[i]); r[i]=n+m,cnt++; c[i]=cnt; id[++C]=i; } for(LL i=n+1;i<=n+m;i++){ scanf("%lld",&o[i]); if(o[i]==1){ scanf("%lld%lld",&a[i],&b[i]); r[i]=n+m,cnt++; id[++C]=i; } if(o[i]==2){ scanf("%lld",&q[i]); r[id[q[i]]]=i,cnt--; } if(o[i]==3){ scanf("%lld",&q[i]); ans[i]=9e18; } c[i]=cnt; } n+=m; for(LL i=1;i<=n;i++)if(o[i]==1)Tr.change(i,r[i],{a[i],b[i]},1); Tr.get(1); for(LL i=1;i<=n;i++) if(o[i]==3)printf("%lld\n",c[i]?ans[i]:-1); } int main() { scanf("%lld",&T); while(T--)init(); }
View Code

由於做此題時參考了我的另一篇部落格[Educational Round 13][Codeforces 678F. Lena and Queries]的程式碼,因此一開始給出的\(n\)個函式我當成了\(n\)次插入來處理。由於我排序時同時對\(b_i\)進行了升序排序,因此在\(a_i\)與上一個函式相同時,當前函式一定不會成為最小值,可以跳過。另外在求“下凸殼”若遇到當前點與st[cnt-1]的"交點"和棧頂與st[cnt-1]的"交點"相同,需要額外比較一下在該點處兩個函式的值以確定留下哪一個函式。