uoj#418. 【集訓隊作業2018】三角形(線段樹合併)
阿新 • • 發佈:2019-01-07
好迷啊……膜一下ljz
考慮每個操作,如果把操作按先後順序放到序列上的話,操作一就是把\(w_i\)的石子放到某個節點,那麼就是在序列末端加入\(w_i\),然後根據貪心肯定要把它所有兒子的石子拿走,也就是要減去\(\sum w_{son}\)
那麼每個點的答案就是序列的最大字首
因為父親節點的操作一要在兒子之後進行,很麻煩,那麼可以每次在自己這裡把\(w_i\)減掉,到父親的時候再加回去
記\((x,y)\)為一個二元組,\(x\)表示當前位置的最大字首和,\(y\)表示最小字尾和,然後定義一個運算\((ax,by)=(x+max(0,a+y),b+min(0,a+y)\),大概能看出是個什麼東西,注意這個運算不滿足交換律
然後考慮一下這些二元組在序列中的順序,如果\(x+y<0\),那麼肯定得放前面,因為可以讓之後的字首和減小。
那麼當\(x+y<0\)時,按\(x\)排序,這樣能使字首和不斷減小。如果\(x+y>0\),按\(y\)排序就好了
維護二元組的話,用線段樹合併,如果當前二元組的\(x+y<0\)且有比它最大字首大的二元組,那麼就已經是最優的了否則將在它前面的二元組與它合併,合併完後加進線段樹裡就好了。注意合併完之後節點沒了要記得清空
//minamoto #include<bits/stdc++.h> #define R register #define int long long #define loli 200000000000000 #define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i) #define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i) #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v) using namespace std; char buf[1<<21],*p1=buf,*p2=buf; inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;} int read(){ R int res,f=1;R char ch; while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1); for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0'); return res*f; } char sr[1<<21],z[20];int C=-1,Z=0; inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;} void print(R int x){ if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x; while(z[++Z]=x%10+48,x/=10); while(sr[++C]=z[Z],--Z);sr[++C]=' '; } const int N=4e5+5; struct eg{int v,nx;}e[N<<1];int head[N],tot; inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;} struct node{ int mx,mn; node(){} node(R int mx,R int mn):mx(mx),mn(mn){} inline node operator +(const node &b){return node(mx+max(0ll,mn+b.mx),b.mn+min(0ll,mn+b.mx));} }tr[N<<5]; int w[N],rt[N],ans[N],ls[N<<5],rs[N<<5]; int n,cnt,fa; void ins(int &p,int l,int r,node x){ if(!p)p=++cnt;if(l==r)return (void)(tr[p]=tr[p]+x); int mid=(l+r)>>1; x.mx<=mid?ins(ls[p],l,mid,x):ins(rs[p],mid+1,r,x); tr[p]=tr[ls[p]]+tr[rs[p]]; } int merge(int x,int y,int l,int r){ if(!x||!y)return x|y;if(l==r)return tr[x]=tr[x]+tr[y],x; int mid=(l+r)>>1; ls[x]=merge(ls[x],ls[y],l,mid),rs[x]=merge(rs[x],rs[y],mid+1,r); tr[x]=tr[ls[x]]+tr[rs[x]]; return x; } void update(int &p,int l,int r,node &x,bool &flag){ if(!p||flag)return; if(l==r){ if(x.mx+x.mn<0&&x.mx<l)return (void)(flag=1); x=x+tr[p],p=0;return; } int mid=(l+r)>>1; update(ls[p],l,mid,x,flag),update(rs[p],mid+1,r,x,flag); if(ls[p]||rs[p])tr[p]=tr[ls[p]]+tr[rs[p]]; else p=0; } void dfs(int u){ node res=node(0,-w[u]); go(u){ res.mx+=w[v],dfs(v); rt[u]=merge(rt[u],rt[v],0,loli); } node qaq=res;bool flag=0; qaq.mx+=w[u],ans[u]=(qaq+tr[rt[u]]).mx; update(rt[u],0,loli,res,flag),ins(rt[u],0,loli,res); } signed main(){ // freopen("testdata.in","r",stdin); read(),n=read(); fp(i,2,n)fa=read(),add(fa,i); fp(i,1,n)w[i]=read(); dfs(1); fp(i,1,n)print(ans[i]); return Ot(),0; }