bzoj3672/luogu2305 购票 (运用点分治思想的树上cdq分治+斜率优化dp)
Posted ressed
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj3672/luogu2305 购票 (运用点分治思想的树上cdq分治+斜率优化dp)相关的知识,希望对你有一定的参考价值。
我们都做过一道题(?)货币兑换,是用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 ",dp[i]); 120 return 0; 121 }
以上是关于bzoj3672/luogu2305 购票 (运用点分治思想的树上cdq分治+斜率优化dp)的主要内容,如果未能解决你的问题,请参考以下文章