「专题总结」凸包
开这个专题时的第一个上午调了一上午多项式,然后进度就一直落后,后来狂追追回来了,所以掌握的不是很扎实。
我讨厌计算几何!!!
凸包其实就是那种斜率优化dp的感觉。多数情况下认为图包维护的是点凸包,而不是直线。
这个我一开始想的是直线,虽然可以做题,但是很多思路都很别扭,甚至和凸包不沾边。。。
还有的问题就是向量叉积来判方向,虽然好像可以用斜率直接替代,但是到了半平面交会吃亏,而且很不工业,所以最好改过来。
$(a,b)X(c,d)=ad-bc$。等于$0$表示平行。大于$0$表示$(a,b)$在$(c,d)$的逆时针方向,否则为顺时针方向。
这样就可以直接用凸包的三个点来判断方向,直接得知中间的那个点是否在凸包内了。
大致按照难度排序。
妖怪:
$Description:$
邱老师是妖怪爱好者,他有n只妖怪,每只妖怪有攻击力atk和防御力dnf两种属性。邱老师立志成为妖怪大师,于是他从真新镇出发,踏上未知的旅途,见识不同的风景。环境对妖怪的战斗力有很大影响,在某种环境中,妖怪可以降低自己k×a点攻击力,提升k×b点防御力或者,提升自己k×a点攻击力,降低k×b点防御力,a,b属于正实数,k为任意实数,但是atk和dnf必须始终非负。妖怪在环境(a,b)中的战斗力为妖怪在该种环境中能达到的最大攻击力和最大防御力之和。strength(a,b)=max(atk(a,b))+max(dnf(a,b))环境由a,b两个参数定义,a,b的含义见前文描述。比如当前环境a=3,b=2,那么攻击力为6,防御力为2的妖怪,能达到的最大攻击力为9,最大防御力为6。所以该妖怪在a=3,b=2的环境下战斗力为15。因此,在不同的环境,战斗力最强的妖怪可能发生变化。作为一名优秀的妖怪训练师,邱老师想发掘每一只妖怪的最大潜力,他想知道在最为不利的情况下,他的n只妖怪能够达到的最强战斗力值,即存在一组正实数(a,b)使得n只妖怪在该环境下最强战斗力最低。$1\le n \le 10^6, 0<atk,dnf \le10^8$
求最小值的最大值。二分答案解方程或三分$\frac{a}{b}$无脑做皆可。卡常卡精。带凸包的做法太过扯淡。。。且不提。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define eps 3e-6 4 int n,a[1000005],b[1000005]; 5 inline int read(){ 6 register int p=0;register char ch=getchar(); 7 while(ch<'0'||ch>'9')ch=getchar(); 8 while(ch>='0'&&ch<='9')p=(p<<3)+(p<<1)+ch-48,ch=getchar(); 9 return p; 10 } 11 int main(){ 12 scanf("%d",&n); 13 for(int i=1;i<=n;++i)a[i]=read(),b[i]=read(); 14 double l=0,r=4e8+5; 15 while(r-l>eps){ 16 double m=(l+r)/2.0,L=0,R=1e18; 17 for(int i=1;i<=n;++i){ 18 double A=a[i],B=a[i]+b[i]-m,C=b[i],D=B*B-4*A*C; 19 if(D<0){L=1e25;break;}D=sqrt(D)/2/A;double Z=-B/A/2; 20 if(Z-D>L)L=Z-D; 21 if(Z+D<R)R=D+Z; 22 if(L>R)break; 23 }if(L<R)r=m;else l=m; 24 }printf("%.4lf",l); 25 }
防线修建:
$Description:$
近来A国和B国的矛盾激化,为了预防不测,A国准备修建一条长长的防线,当然修建防线的话,肯定要把需要保护的城市修在防线内部了。可是A国上层现在还犹豫不决,到底该把哪些城市作为保护对象呢?又由于A国的经费有限,所以希望你能帮忙完成如下的一个任务:
1.给出你所有的A国城市坐标
2.A国上层经过讨论,考虑到经济问题,决定取消对i城市的保护,也就是说i城市不需要在防线内了
3.A国上层询问对于剩下要保护的城市,修建防线的总经费最少是多少
你需要对每次询问作出回答。注意单位1长度的防线花费为1。
A国的地形是这样的,形如下图,x轴是一条河流,相当于一条天然防线,不需要你再修建
A国总是有两个城市在河边,一个点是(0,0),一个点是(n,0),其余所有点的横坐标均大于0小于n,纵坐标均大于0。A国有一个不在(0,0)和(n,0)的首都。(0,0),(n,0)和首都这三个城市是一定需要保护的。$m \le 100000,q \le 200000$
所有点的坐标范围均在$10^4$以内, 数据保证没有重点
作为模板题,出题人给出了充分的人性化的各种保证,做起来非常方便,大赞。
用$set$维护也很好做。时光倒流一下就是就变成插入了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 set<pair<int,int> >S; 4 int X,Y,x[123456],y[123456],o[222222],p[222222],tp,n,m,q,D[123456]; 5 double Ans,ans[222222]; 6 double cal(int x,int y){return sqrt(1ll*x*x+1ll*y*y);} 7 void add(int x,int y){ 8 auto p=S.lower_bound(make_pair(x,y)); 9 int xn=(*p).first,yn=(*p).second,xl=(*(--p)).first,yl=(*p).second; 10 if((y-yl)*(xn-x)<=(yn-y)*(x-xl))return; 11 Ans+=cal(x-xl,y-yl)+cal(xn-x,yn-y)-cal(xn-xl,yn-yl);S.insert(make_pair(x,y)); 12 p=--S.lower_bound(make_pair(x,y));xn=x;yn=y; 13 while(p!=S.begin()){ 14 x=(*p).first,y=(*p).second,xl=(*(--p)).first,yl=(*p).second; 15 if((y-yl)*(xn-x)>(yn-y)*(x-xl))break; 16 Ans-=cal(xl-x,yl-y)+cal(x-xn,y-yn)-cal(xl-xn,yl-yn); 17 S.erase(make_pair(x,y));p=S.find(make_pair(xl,yl)); 18 }xl=xn;yl=yn;p=S.upper_bound(make_pair(xl,yl)); 19 while(p!=(--S.end())){ 20 x=(*p).first,y=(*p).second,xn=(*(++p)).first,yn=(*p).second; 21 if((y-yl)*(xn-x)>(yn-y)*(x-xl))break; 22 Ans-=cal(xl-x,yl-y)+cal(x-xn,y-yn)-cal(xl-xn,yl-yn); 23 S.erase(make_pair(x,y));p=S.find(make_pair(xn,yn)); 24 } 25 } 26 int main(){//freopen("1.in","r",stdin); 27 cin>>n>>X>>Y>>m; 28 for(int i=1;i<=m;++i)scanf("%d%d",&x[i],&y[i]); 29 cin>>q; for(int i=1;i<=q;++i){scanf("%d",&o[i]);if(o[i]==1)scanf("%d",&p[i]),D[p[i]]=1;} 30 S.insert(make_pair(0,0));S.insert(make_pair(n,0));S.insert(make_pair(X,Y)); 31 Ans=cal(X,Y)+cal(n-X,Y); 32 for(int i=1;i<=m;++i)if(!D[i])add(x[i],y[i]); 33 for(int i=q;i;--i)if(o[i]==1)add(x[p[i]],y[p[i]]);else ans[++tp]=Ans; 34 while(tp)printf("%.2lf\n",ans[tp]),tp--; 35 }
向量集:
$Description:$
维护一个向量集合,在线支持以下操作:
"A x y (|x|,|y| < =10^8)":加入向量(x,y);
" Q x y l r (|x|,|y| < =10^8,1 < =L < =R < =T,其中T为已经加入的向量个数)询问第L个到第R个加入的向量与向量(x,y)的点积的最大值。
集合初始时为空。$n \le 4 \times 10^5 $
只有加入没有删除和修改。查询区间最大值。
把每次查询时的$max(xw+yz)$变为$max(x \frac{w}{z}+y) \times z$。分$z$正负讨论。
线段树。维护上下两个凸包。在凸包上二分/三分最值。二分可以根据斜率来。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define int long long 4 #define S 400005 5 #define inf 0x3fffffffffffffff 6 int n,la,lar,x[S],y[S],cnt;char O[2]; 7 struct P{int x,y;friend bool operator<(P a,P b){return a.x<b.x;}}ps[S]; 8 vector<int>vx[2][S<<2],vy[2][S<<2]; 9 void modify(int X,int p=1,int cl=1,int cr=n){ 10 if(cr==X){int L=cr-cl+1,t=-1,X,Y; 11 for(int i=cl;i<=cr;++i)ps[i-cl+1]=(P){x[i],y[i]}; 12 sort(ps+1,ps+1+L); 13 for(int i=1;i<=L;++i){X=ps[i].x;Y=ps[i].y; 14 while(~t&&vx[0][p][t]==X&&vy[0][p][t]<=Y)t--,vx[0][p].pop_back(),vy[0][p].pop_back(); 15 if(~t&&vx[0][p][t]==X&&vy[0][p][t]>Y)continue; 16 while(t>0&&(vy[0][p][t]-vy[0][p][t-1])*(X-vx[0][p][t])<=(Y-vy[0][p][t])*(vx[0][p][t]-vx[0][p][t-1]))vx[0][p].pop_back(),vy[0][p].pop_back(),t--; 17 vx[0][p].push_back(X);vy[0][p].push_back(Y);t++; 18 }t=-1; 19 for(int i=1;i<=L;++i){X=ps[i].x;Y=ps[i].y; 20 while(~t&&vx[1][p][t]==X&&vy[1][p][t]>=Y)t--,vx[1][p].pop_back(),vy[1][p].pop_back(); 21 if(~t&&vx[1][p][t]==X&&vy[1][p][t]<Y)continue; 22 while(t>0&&(vy[1][p][t]-vy[1][p][t-1])*(X-vx[1][p][t])>=(Y-vy[1][p][t])*(vx[1][p][t]-vx[1][p][t-1]))vx[1][p].pop_back(),vy[1][p].pop_back(),t--; 23 vx[1][p].push_back(X);vy[1][p].push_back(Y);t++; 24 } 25 }if(cl==cr)return; 26 if(X>cl+cr>>1)modify(X,p<<1|1,(cl+cr>>1)+1,cr); 27 else modify(X,p<<1,cl,cl+cr>>1); 28 } 29 int cal(int o,int p,int s,int X,int Y){ 30 if(s>=vx[o][p].size())return -inf; 31 return vx[o][p][s]*X+vy[o][p][s]*Y; 32 } 33 int ask(int l,int r,int X,int Y,int p=1,int cl=1,int cr=n){ 34 if(l<=cl&&cr<=r){ 35 int o=Y<0,L=0,R=vx[o][p].size()-2,ans=-inf,ap=0,md; 36 while(md=L+R>>1,L<=R)if(cal(o,p,md,X,Y)<cal(o,p,md+1,X,Y))L=ap=md,L++;else R=(L+R>>1)-1; 37 for(int i=0;i<3;++i)ans=max(ans,cal(o,p,ap+i,X,Y));return ans; 38 }return max(l<=cl+cr>>1?ask(l,r,X,Y,p<<1,cl,cl+cr>>1):-inf,r>cl+cr>>1?ask(l,r,X,Y,p<<1|1,(cl+cr>>1)+1,cr):-inf); 39 } 40 signed main(){//freopen("1.in","r",stdin);freopen("my.out","w",stdout); 41 scanf("%lld%s",&n,O); 42 if(O[0]^'E')lar=1; 43 for(int i=1,X,Y,L,R;i<=n;++i){ 44 scanf("%s%lld%lld",O,&X,&Y);X^=la;Y^=la; 45 if(O[0]=='A')x[++cnt]=X,y[cnt]=Y,modify(cnt); 46 else{scanf("%lld%lld",&L,&R);L^=la;R^=la; 47 printf("%lld\n",la=ask(L,R,X,Y));la*=lar;la&=0x7fffffff; 48 } 49 } 50 }
货币兑换cash:
$Description:$
小Y最近在一家金券交易所工作。该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下简称B券)。每个持有金券的顾客都有一个自己的帐户。金券的数目可以是一个实数。每天随着市场的起伏波动,两种金券都有自己当时的价值,即每一单位金券当天可以兑换的人民币数目。我们记录第 K 天中 A券 和 B券 的价值分别为 AK 和 BK(元/单位金券)。为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易法。比例交易法分为两个方面:(a)卖出金券:顾客提供一个 [0,100] 内的实数 OP 作为卖出比例,其意义为:将OP% 的 A券和 OP% 的 B券 以当时的价值兑换为人民币;(b)买入金券:顾客支付 IP 元人民币,交易所将会兑换给用户总价值为 IP 的金券,并且,满足提供给顾客的A券和B券的比例在第 K 天恰好为 RateK;例如,假定接下来 3 天内的 Ak、Bk、RateK 的变化分别为:
假定在第一天时,用户手中有 100元 人民币但是没有任何金券。用户可以执行以下的操作:
注意到,同一天内可以进行多次操作。小Y是一个很有经济头脑的员工,通过较长时间的运作和行情测算,他已经知道了未来N天内的A券和B券的价值以及Rate。他还希望能够计算出来,如果开始时拥有S元钱,那么N天后最多能够获得多少元钱。$n \le 10^5,Maxprofit \le 10^9$
dp。化简一下式子就和上一题差不多了。只用维护一个凸包。
但是这个有动态插入而查询全局,所以用一个$splay$。在$splay$上二分找最优解。
按照$x$建$splay$之后斜率也是有序的,询问时按照斜率跑就可以了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 111111 4 #define eps 1e-8 5 #define lc c[0][p] 6 #define rc c[1][p] 7 int f[S],c[2][S],rt,pc;double X[S],Y[S],dp[S],A[S],B[S],R,K[S]; 8 void spin(int p){ 9 int F=f[p],G=f[F],d=c[1][F]==p,B=c[!d][p]; 10 if(G)c[c[1][G]==F][G]=p; c[d][c[!d][p]=F]=B; 11 f[f[F]=p]=G; if(B)f[B]=F; 12 } 13 void splay(int p){for(int F;F=f[p];spin(p))if(f[F])spin(c[1][f[F]]==F^c[1][F]==p?p:F);rt=p;} 14 int pre(int p){p=lc;while(rc)p=rc;return p;} 15 int suc(int p){p=rc;while(lc)p=lc;return p;} 16 double cal(int i,int j){return A[j]*X[i]+B[j]*Y[i];} 17 double calK(int a,int b){return fabs(X[a]-X[b])<eps?-1e9:(Y[a]-Y[b])/(X[a]-X[b]);} 18 double ask(int p,int o,double k){double A=cal(p,o); 19 while(lc|rc)p=lc&&rc?c[K[p]>k][p]:lc|rc,A=max(A,cal(p,o)); 20 splay(p);return A; 21 } 22 void del(int p){ 23 splay(p); if(!(lc*rc)){f[rt=lc|rc]=0;return;} 24 int pr=pre(p);splay(pr);c[1][f[rc]=rt]=rc; 25 } 26 void sat(){ 27 int p=rt,l=pre(p),r=suc(p),t; 28 if(l&&r&&(X[l]-X[p])*(Y[r]-Y[p])<(X[r]-X[p])*(Y[l]-Y[p])){del(p);return;} 29 while(splay(t=l),(l=pre(t))&&(X[l]-X[p])*(Y[t]-Y[p])>(X[t]-X[p])*(Y[l]-Y[p]))del(t); 30 while(splay(t=r),(r=suc(t))&&(X[r]-X[p])*(Y[t]-Y[p])<(X[t]-X[p])*(Y[r]-Y[p]))del(t); 31 splay(p);l=pre(p);r=suc(p);K[p]=calK(l,p);K[r]=calK(p,r); 32 } 33 void add(double x,double y){ 34 int p=rt,F; if(!p){X[rt=++pc]=x;Y[rt]=y;return;} 35 while(p&&fabs(X[p]-x)+fabs(Y[p]-y)>eps)F=p,p=c[X[p]<x][p]; 36 if(!p)X[++pc]=x,Y[pc]=y,c[x>X[F]][f[pc]=F]=pc,splay(pc),sat(); 37 } 38 int main(){ 39 int n;cin>>n>>dp[0]; 40 for(int i=1;i<=n;++i)scanf("%lf%lf%lf",&A[i],&B[i],&R),dp[i]=max(dp[i-1],ask(rt,i,-A[i]/B[i])), 41 add(dp[i]*R/(R*A[i]+B[i]),dp[i]/(R*A[i]+B[i])); 42 printf("%.3lf\n",dp[n]); 43 }
购票:
$Description:$
今年夏天,NOI在SZ市迎来了她30周岁的生日。来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会。
全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接。为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号。其中SZ市的编号为 1。对于除SZ市之外的任意一个城市 v,我们给出了它在这棵树上的父亲城市 fv 以及到父亲城市道路的长度 sv。
从城市 v 前往SZ市的方法为:选择城市 v 的一个祖先 a,支付购票的费用,乘坐交通工具到达 a。再选择城市 a 的一个祖先 b,支付费用并到达 b。以此类推,直至到达SZ市。
对于任意一个城市 v,我们会给出一个交通工具的距离限制 lv。对于城市 v 的祖先 a,只有当它们之间所有道路的总长度不超过 lv 时,从城市 v 才可以通过一次购票到达城市 a,否则不能通过一次购票到达。对于每个城市 v,我们还会给出两个非负整数 pv,qv 作为票价参数。若城市 v 到城市 a 所有道路的总长度为 d,那么从城市 v 到城市 a 购买的票价为 dpv+qv。
每个城市的OIer都希望自己到达SZ市时,用于购票的总资金最少。你的任务就是,告诉每个城市的OIer他们所花的最少资金是多少。$n \le 2 \times 10^5,p \le 10^6,q \le 10^{12},s \le l \le 10^{11}$
复杂度总是让人不敢写,然而的确就是正解。$O(nlog^3\ n)$
树剖维护链信息,对于距离的限制在树上二分一下也就可以了。
就和向量集一样了。按照$dfs$序$dp$下去就好了。
要注意中间算叉积的时候可能会炸$long \ long$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 200005 4 #define int long long 5 int pr[S],q[S],li[S],dp[S],dep[S],sz[S],hson[S],l[S],to[S],fir[S],ec,f[S],top[S]; 6 int dfn[S],idfn[S],tim,n,sta[S],sp[S],TP,alc[S<<2]; 7 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;} 8 void dfs(int p){ 9 sz[p]=1;dep[p]+=dep[f[p]]; 10 for(int i=fir[p];i;i=l[i]){ 11 dfs(to[i]);sz[p]+=sz[to[i]]; 12 if(sz[to[i]]>sz[hson[p]])hson[p]=to[i]; 13 } 14 } 15 void DFS(int p,int tp){ 16 top[p]=tp;dfn[p]=++tim;idfn[tim]=p; 17 if(hson[p])DFS(hson[p],tp); 18 for(int i=fir[p];i;i=l[i])if(!dfn[to[i]])DFS(to[i],to[i]); 19 } 20 vector<int>x[S<<2],y[S<<2]; 21 void update(int X,int p=1,int cl=1,int cr=n){ 22 alc[p]++;if(alc[p]==cr-cl+1){int t=-1; 23 for(int i=cr;i>=cl;--i){ 24 int _x=-dep[idfn[i]],_y=dp[idfn[i]]; 25 while(t>0&&(0.0L+y[p][t]-y[p][t-1])*(_x-x[p][t])>(0.0L+_y-y[p][t])*(x[p][t]-x[p][t-1]))t--,x[p].pop_back(),y[p].pop_back(); 26 ++t;x[p].push_back(_x);y[p].push_back(_y); 27 } 28 } 29 if(cl==cr)return; 30 if(X>cl+cr>>1)update(X,p<<1|1,(cl+cr>>1)+1,cr); 31 else update(X,p<<1,cl,cl+cr>>1); 32 } 33 int cal(int p,int s,int P){ 34 if(s>=x[p].size())return 0; 35 int w=pr[P]*x[p][s]+y[p][s]; dp[P]=min(dp[P],w); return w; 36 } 37 void ask(int l,int r,int P,int p=1,int cl=1,int cr=n){ 38 if(l<=cl&&cr<=r){ 39 int L=0,R=x[p].size()-2,ans=0,md; 40 while(md=L+R>>1,L<=R)if(cal(p,md,P)>cal(p,md+1,P))ans=L=md,L++;else R=md-1; 41 for(int i=0;i<=2;++i)cal(p,ans+i,P);return; 42 } 43 if(l<=cl+cr>>1)ask(l,r,P,p<<1,cl,cl+cr>>1); 44 if(r>cl+cr>>1)ask(l,r,P,p<<1|1,(cl+cr>>1)+1,cr); 45 } 46 void query(int T,int E,int p){ 47 while(top[T]!=top[E])ask(dfn[top[E]],dfn[E],p),E=f[top[E]]; 48 ask(dfn[T],dfn[E],p); 49 } 50 void Dfs(int p){ 51 if(p^1)dp[p]=1e18,query(sp[lower_bound(sta+1,sta+1+TP,dep[p]-li[p])-sta],f[p],p),dp[p]+=dep[p]*pr[p]+q[p]; 52 sta[++TP]=dep[p];sp[TP]=p;update(dfn[p]); 53 for(int i=fir[p];i;i=l[i])Dfs(to[i]);TP--; 54 } 55 main(){ 56 scanf("%lld%*d",&n); 57 for(int i=2;i<=n;++i)scanf("%lld%lld%lld%lld%lld",&f[i],&dep[i],&pr[i],&q[i],&li[i]),link(f[i],i); 58 dfs(1);DFS(1,1);Dfs(1); 59 for(int i=2;i<=n;++i)printf("%lld\n",dp[i]); 60 }
陶陶的难题II:
$Description:$
$N,M \le 3\times 10^4,x,y,p,q\le 10^8$
数据范围很小。。。猜复杂度?$O(n \ log^4 n)$
看到分数形式不难想到二分答案(01分数规划)。树结构链操作修改依旧是树链剖分。找最值依旧是凸包上二分或三分。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define lc p<<1 4 #define rc p<<1|1 5 #define S 33333 6 #define eps 1e-7 7 int fir[S],l[S<<1],to[S<<1],ec,sz[S],dep[S],hson[S],dfn[S],idfn[S],top[S],tim,f[S]; 8 int ps[S],st,n; 9 double A[2][S],B[2][S]; 10 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;} 11 void dfs(int p,int fa){sz[p]=1;dep[p]=dep[fa]+1;f[p]=fa; 12 for(int i=fir[p];i;i=l[i])if(to[i]!=fa){ 13 dfs(to[i],p),sz[p]+=sz[to[i]]; 14 if(sz[to[i]]>sz[hson[p]])hson[p]=to[i]; 15 } 16 } 17 void DFS(int p,int tp){ 18 dfn[p]=++tim;idfn[tim]=p;top[p]=tp; 19 if(hson[p])DFS(hson[p],tp); 20 for(int i=fir[p];i;i=l[i])if(to[i]!=f[p]&&to[i]!=hson[p])DFS(to[i],to[i]); 21 } 22 vector<int>v[2][S<<2]; 23 struct Line{double k,b;int o;friend bool operator<(Line a,Line b){return a.k<b.k;}}L[S]; 24 void build(int p,int cl,int cr){ 25 if(cl==cr){v[0][p].push_back(idfn[cl]);v[1][p].push_back(idfn[cl]);return;} 26 build(lc,cl,cl+cr>>1); build(rc,(cl+cr>>1)+1,cr); 27 int c=cr-cl+1; 28 for(int o=0;o<2;++o){ 29 for(int i=0;i<c;++i)L[i+1]=(Line){A[o][idfn[cl+i]],-B[o][idfn[cl+i]],idfn[cl+i]}; 30 sort(L+1,L+c+1);int tp=-1; 31 for(int i=1;i<=c;++i){ 32 double k3=L[i].k,b3=L[i].b; 33 while(tp>0){ 34 double k1=A[o][v[o][p][tp-1]],b1=-B[o][v[o][p][tp-1]],k2=A[o][v[o][p][tp]],b2=-B[o][v[o][p][tp]]; 35 if((b3-b1)*(k2-k3)+eps>(b3-b2)*(k1-k3))v[o][p].pop_back(),tp--;else break; 36 }tp++;v[o][p].push_back(L[i].o); 37 } 38 } 39 } 40 void query(int l,int r,int p=1,int cl=1,int cr=n){ 41 if(l<=cl&&cr<=r){ps[++st]=p;return;} 42 if(l<=cl+cr>>1)query(l,r,lc,cl,cl+cr>>1); 43 if(r>cl+cr>>1)query(l,r,rc,(cl+cr>>1)+1,cr); 44 } 45 void Query(int x,int y){ 46 while(top[x]!=top[y])if(dep[top[x]]>dep[top[y]])query(dfn[top[x]],dfn[x]),x=f[top[x]]; 47 else query(dfn[top[y]],dfn[y]),y=f[top[y]]; 48 return dep[x]>dep[y]?query(dfn[y],dfn[x]):query(dfn[x],dfn[y]); 49 } 50 int main(){cin>>n; 51 for(int o=0;o<2;++o){ 52 for(int i=1;i<=n;++i)scanf("%lf",&B[o][i]); 53 for(int i=1;i<=n;++i)scanf("%lf",&A[o][i]); 54 }for(int i=1,x,y;i<n;++i)scanf("%d%d",&x,&y),link(x,y),link(y,x); 55 dfs(1,0);DFS(1,1);build(1,1,n); int m;cin>>m; 56 for(int i=1,x,y;i<=m;++i){ 57 scanf("%d%d",&x,&y);st=0;Query(x,y); 58 double l=0,r=2e5,M,W[2]; 59 while(r-l>1e-5){M=(l+r)/2;W[0]=W[1]=-1e8; 60 for(int o=0;o<2;++o)for(int s=1;s<=st;++s){ 61 int L=1,R=v[o][ps[s]].size()-1,md,ans; 62 while(L<=R-4){ 63 md=L+R>>1; 64 if(A[o][v[o][ps[s]][md-1]]-B[o][v[o][ps[s]][md-1]]*M+eps>A[o][v[o][ps[s]][md]]-B[o][v[o][ps[s]][md]]*M)R=md;else L=md-1; 65 } 66 for(int j=L-1;j<=R;++j)W[o]=max(W[o],A[o][v[o][ps[s]][j]]-B[o][v[o][ps[s]][j]]*M); 67 } 68 if(W[0]+W[1]>0)l=M;else r=M; 69 }printf("%.4lf\n",l); 70 } 71 }
旅行规划:
$Description:$
OIVillage是一个风景秀美的乡村,为了更好的利用当地的旅游资源,吸引游客,推动经济发展,xkszltl决定修建了一条铁路将当地n个最著名的经典连接起来,让游客可以通过火车从铁路起点(1号景点)出发,依次游览每个景区。为了更好的评价这条铁路,xkszltl为每一个景区都哦赋予了一个美观度,而一条旅行路径的价值就是它所经过的景区的美观度之和。不过,随着天气与季节的变化,某些景点的美观度也会发生变化。
xkszltl希望为每位旅客提供最佳的旅行指导,但是由于游客的时间有限,不一定能游览全部景区,然而他们也不希望旅途过于短暂,所以每个游客都希望能在某一个区间内的车站结束旅程,而xkszltl的任务就是为他们选择一个终点使得旅行线路的价值最大。可是当地的景点与前来观光的旅客实在是太多了,xkszltl无法及时完成任务,于是找到了准备虐杀NOI2011的你,希望你能帮助他完成这个艰巨的任务。$n,m\le 10^5$
这个是真的没想到。。。我思维僵化的一大体现。。。永远不会往这个方向想。
最蠢的是我看错题了,我以为这是一棵树。。。。
对于全部数据,树退化成一条链。
然后颓了复杂度,$O(n\sqrt{n} log \ n)$。一切显然。
前缀和是比较明显的。操作是区间加等差数列,区间查最大值。
每次修改时非整块暴力修改整块重建,修改整块的话记录一下首项与公差。
分块,每个点以下标为横坐标,发现要找的切线的斜率就是公差。可以维护了。
分块真的想不到,加等差数列转化成凸包也想不到。。。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define inf 0x3fffffffffffffff 4 #define int long long 5 int n,m,B,v[111111],mx[111111],A[111111],D[111111],cl[111111],cr[111111],bl[111111],bc; 6 vector<int>x[111111],y[111111];vector<double>K[111111]; 7 int cal(int p,int w){return x[p][w]*D[p]+y[p][w];} 8 void recal(int p){ 9 int ps=lower_bound(K[p].begin(),K[p].end(),D[p])-K[p].begin(); 10 mx[p]=cal(p,ps);if(ps)mx[p]=max(mx[p],cal(p,ps-1));if(ps+1<x[p].size())mx[p]=max(mx[p],cal(p,ps+1)); 11 mx[p]+=A[p];//for(int i=0;i<x[p].size();++i)cout<<x[p][i]<<' '<<y[p][i]<<endl;cout<<A[p]<<' '<<D[p]<<' '<<ps<<"!!!\n"; 12 } 13 void rebuild(int k){mx[k]=-inf; 14 for(int i=cl[k];i<=cr[k];++i)v[i]+=D[k]*(i-cl[k])+A[k],mx[k]=max(mx[k],v[i]); D[k]=A[k]=0; 15 x[k].clear();y[k].clear();K[k].clear(); 16 for(int i=cl[k];i<=cr[k];++i){int T=(int)(x[k].size())-1,X=i-cl[k],Y=v[i]; 17 while(T>0&&(Y-y[k][T])*(X-x[k][T-1])>(Y-y[k][T-1])*(X-x[k][T]))T--,x[k].pop_back(),y[k].pop_back(); 18 x[k].push_back(X);y[k].push_back(Y); 19 }for(int i=1;i<x[k].size();++i)K[k].push_back(-1.0*(y[k][i]-y[k][i-1])/(x[k][i]-x[k][i-1])); 20 } 21 main(){//freopen("1.in","r",stdin);freopen("2.out","w",stdout); 22 cin>>n;B=sqrt(n); 23 bc=n/B+(n%B?1:0); 24 for(int i=1;i<=bc;++i)cl[i]=i*B-B+1,cr[i]=i*B,mx[i]=-inf; 25 cr[bc]=n; 26 for(int i=1;i<=bc;++i)for(int j=cl[i];j<=cr[i];++j)bl[j]=i; 27 for(int i=1;i<=n;++i)scanf("%lld",&v[i]),v[i]+=v[i-1]; 28 for(int i=1;i<=bc;++i)rebuild(i); 29 cin>>m; 30 for(int i=1,o,x,y,d;i<=m;++i){ 31 scanf("%lld%lld%lld",&o,&x,&y); 32 if(!o){scanf("%lld",&d);x--; 33 if(bl[x+1]==bl[y]){for(int i=x+1;i<=y;++i)v[i]+=d*(i-x);rebuild(bl[y]);goto Sec2;} 34 for(int i=x+1;i<=cr[bl[x+1]];++i)v[i]+=d*(i-x);rebuild(bl[x+1]); 35 for(int i=cl[bl[y]];i<=y;++i)v[i]+=d*(i-x);rebuild(bl[y]); 36 for(int i=bl[x+1]+1;i<=bl[y]-1;++i)A[i]+=d*(cl[i]-x),D[i]+=d,recal(i); 37 Sec2:for(int i=y+1;i<=cr[bl[y]];++i)v[i]+=d*(y-x);rebuild(bl[y]); 38 for(int i=bl[y]+1;i<=bc;++i)A[i]+=d*(y-x),mx[i]+=d*(y-x); 39 }else{int ans=-inf; 40 if(bl[x]==bl[y]){ 41 for(int i=x;i<=y;++i)ans=max(ans,v[i]+(i-cl[bl[i]])*D[bl[i]]+A[bl[i]]); 42 printf("%lld\n",ans);continue; 43 }for(int i=x;i<=cr[bl[x]];++i)ans=max(ans,v[i]+(i-cl[bl[i]])*D[bl[i]]+A[bl[i]]); 44 for(int i=cl[bl[y]];i<=y;++i)ans=max(ans,v[i]+(i-cl[bl[i]])*D[bl[i]]+A[bl[i]]);//cout<<bl[x]<<' '<<bl[y]<<' '<<ans<<endl; 45 for(int i=bl[x]+1;i<=bl[y]-1;++i)ans=max(ans,mx[i]);printf("%lld\n",ans); 46 }//for(int i=1;i<=bc;++i)cout<<cl[i]<<' '<<cr[i]<<' '<<mx[i]<<' '<<endl; 47 } 48 }