1. 程式人生 > >bzoj3672/luogu2305 購票 (運用點分治思想的樹上cdq分治+斜率優化dp)

bzoj3672/luogu2305 購票 (運用點分治思想的樹上cdq分治+斜率優化dp)

lse oid 現在 targe tchar .com nbsp char tro

我們都做過一道題(?)貨幣兌換,是用cdq分治來解決不單調的斜率優化

現在它放到了樹上..

總之先寫下來dp方程,$f[i]=min\{f[j]+(dis[i]-dis[j])*p[i]+q[i]\} ,j是i的祖先,dis[i]-dis[j]<=l[i]$ ,其中dis[i]表示1號點到i號點的距離

可以很明顯的看出斜率優化,但我們要放到樹上做

於是就運用點分治的思想來找重心(正如普通的cdq是找重點一樣)

步驟是這樣的:

1.對於根為x的一個子樹,我們先找到它的重心rt

2.把rt的子樹刨掉,但留下rt,然後遞歸地再做x,目的是先算出祖先們的答案,為更新孩子們做準備

3.把rt在子樹x中的祖先按深度排序,把rt的子樹(不包括rt)按照它們最遠能到的祖先的深度排序,然後一邊插祖先維護一個凸包,一邊給孩子統計答案

4.遞歸地做rt的子節點

斜率優化的細節就不寫了(因為全是蒙出來的)

  1 #include<bits/stdc++.h>
  2 #define pa pair<int,int>
  3 #define ll long long
  4 using namespace std;
  5 const int maxn=2e5+10,inf=0x3f3f3f3f;
  6 
  7 inline ll rd(){
  8     ll x=0;char c=getchar();int neg=1;
  9     while(c<0||c>
9){if(c==-) neg=-1;c=getchar();} 10 while(c>=0&&c<=9) x=x*10+c-0,c=getchar(); 11 return x*neg; 12 } 13 14 struct Edge{ 15 int b,ne;ll l; 16 }eg[maxn*2]; 17 int fa[maxn][20],egh[maxn],ect; 18 int N,NN,dep[maxn],ldep[maxn]; 19 int siz[maxn],root; 20 int
cnt[maxn],tmpl[maxn],tmpr[maxn],pct; 21 int stk[maxn]; 22 ll dp[maxn],dis[maxn],p[maxn],q[maxn],l[maxn]; 23 bool vis[maxn]; 24 25 void dfs1(int x){ 26 int i; 27 for(i=1;fa[x][i-1]&&fa[fa[x][i-1]][i-1];i++) fa[x][i]=fa[fa[x][i-1]][i-1]; 28 int y=x;for(ll j=l[x];i>=0;i--){ 29 if(fa[y][i]&&dis[y]-dis[fa[y][i]]<=j) 30 j-=dis[y]-dis[fa[y][i]],y=fa[y][i]; 31 } 32 if(y!=x) ldep[x]=dep[y]; 33 else ldep[x]=-1; 34 35 for(i=egh[x];i;i=eg[i].ne){ 36 int b=eg[i].b; 37 dis[b]=dis[x]+eg[i].l; 38 dep[b]=dep[x]+1; 39 dfs1(b); 40 } 41 } 42 43 void getroot(int x,int smsiz,int &ms,int &root){ 44 siz[x]=1;int mm=0; 45 for(int i=egh[x];i;i=eg[i].ne){ 46 int b=eg[i].b;if(vis[b]) continue; 47 getroot(b,smsiz,ms,root);siz[x]+=siz[b]; 48 mm=max(mm,siz[b]); 49 }mm=max(mm,smsiz-siz[x]); 50 if(mm<=ms) ms=mm,root=x; 51 } 52 void getson(int x){ 53 tmpr[++pct]=x; 54 for(int i=egh[x];i;i=eg[i].ne){ 55 int b=eg[i].b;if(vis[b]) continue; 56 getson(b); 57 } 58 } 59 60 inline bool cmp(int a,int b){ 61 return ldep[a]>ldep[b]; 62 } 63 64 inline double getk(int a,int b){ 65 return (double)(dp[a]-dp[b])/(dis[a]-dis[b]); 66 } 67 68 void cdq(int x,int s){ 69 if(s<=1) return; 70 int rt,ms=inf; 71 getroot(x,s,ms,rt); 72 if(x!=rt){ 73 int sms=s,mmm=inf; 74 for(int i=egh[rt];i;i=eg[i].ne) vis[eg[i].b]=1,sms-=siz[eg[i].b]; 75 cdq(x,sms); 76 }int l=0; 77 for(int i=rt;i!=fa[x][0];i=fa[i][0]) tmpl[++l]=i; 78 pct=0; 79 for(int i=egh[rt];i;i=eg[i].ne) getson(eg[i].b); 80 sort(tmpr+1,tmpr+pct+1,cmp); 81 82 int sh=0; 83 for(int i=1,j=1;i<=pct&&ldep[tmpr[i]]!=-1;i++){ 84 for(;j<=l&&dep[tmpl[j]]>=ldep[tmpr[i]];j++){ 85 while(sh>1&&getk(stk[sh],stk[sh-1])<=getk(stk[sh],tmpl[j])) --sh; 86 stk[++sh]=tmpl[j]; 87 } 88 if(sh){ 89 int k=stk[1]; 90 if(sh!=1){ 91 int a=1,b=sh-1; 92 while(a<=b){ 93 int m=(a+b)>>1; 94 if(getk(stk[m],stk[m+1])<=p[tmpr[i]]) k=stk[m],b=m-1; 95 else k=stk[m+1],a=m+1; 96 } 97 } 98 dp[tmpr[i]]=min(dp[tmpr[i]],dp[k]+(dis[tmpr[i]]-dis[k])*p[tmpr[i]]+q[tmpr[i]]); 99 } 100 } 101 for(int i=egh[rt];i;i=eg[i].ne){ 102 cdq(eg[i].b,siz[eg[i].b]); 103 } 104 } 105 106 inline void adeg(int a,int b,int l){ 107 eg[++ect].b=b;eg[ect].l=l;eg[ect].ne=egh[a];egh[a]=ect; 108 } 109 110 int main(){ 111 int i,j,k; 112 N=rd();rd(); 113 for(i=2;i<=N;i++){ 114 int a=rd(),b=rd();p[i]=rd(),q[i]=rd(),l[i]=rd(); 115 adeg(a,i,b);fa[i][0]=a; 116 }ldep[0]=-1;dep[1]=1;dfs1(1); 117 memset(dp,127,sizeof(dp));dp[1]=0; 118 cdq(1,N); 119 for(i=2;i<=N;i++) printf("%lld\n",dp[i]); 120 return 0; 121 }

bzoj3672/luogu2305 購票 (運用點分治思想的樹上cdq分治+斜率優化dp)