解题:NOI 2014 购票

题面

 观察一下部分分,我们发现链上的部分分是这样一个DP:

 $dp[i]=min(dp[i],dp[j]+dis(i,j)*p[i]+q[i])(dis(i,j)<=lim[i]&&j∈anc(i))$

对于可以对$i$转移的两个位置$j$和$k$,假设$dep[j]>dep[k]$且$j$比$k$优,那么倒腾一下式子

$dp[j]+dis(i,j)*p[i]+q[i]<dp[k]+dis(i,k)*p[i]+q[i]$

$dp[j]-dp[k]<(dis(i,k)-dis(i,j))*p[i]$

$frac{dp[j]-dp[k]}{dis(i,k)-dis(i,j)}<p[i]$

因为$dis$是单调的,这个东西可以使用斜率优化,因为$p[i]$不单调每次需要二分来转移

然后问题来了,现在不是棵树,如果提出来每条链来做是O(叶子数*len)的,会被扫帚图卡掉,于是有了各种优化来做这道题:

Sol1:用轻重链剖分优化,直接以一个log的代价把整条链拼出来,再用一个log的数据结构维护,复杂度$O(nlog^3 n)$

Sol2:用可持久化数据结构维护单调队列或者用待撤销的二进制分组,利用上面一段已经处理好的链分别处理每个叶子,只需要一个log,复杂度$O(nlog^2 n)$

Sol3(我写的这个):不用数据结构维护,用点分治的方法,每次把重心和它子树外的部分递归处理,然后DFS重心的子树,子树里的节点按向上延伸后的深度排序后DP,之后再处理每一棵子树。排序和DP的log是并列的,复杂度$O(nlog^2 n)$

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=200005,inf=1e9;
 6 int n,t,c,cnt,top,tpp,tsiz,maxx;
 7 int fth[N],p[N],noww[N],goal[N],siz[N],vis[N],stk[N],sta[N];
 8 long long len[N],val[N],dis[N],pr1[N],pr2[N],lim[N],dp[N],rd;
 9 bool cmp(int a,int b)
10 {
11     return dis[a]-lim[a]>dis[b]-lim[b];
12 }
13 double Slope(int a,int b)
14 {
15     return (double)(dp[b]-dp[a])/(double)(dis[b]-dis[a]);
16 }
17 void Link(int f,int t,long long v)
18 {
19     noww[++cnt]=p[f],p[f]=cnt;
20     goal[cnt]=t,val[cnt]=v;
21 }
22 void Getdis(int nde)
23 {
24     for(int i=p[nde];i;i=noww[i])
25         dis[goal[i]]=dis[nde]+val[i],Getdis(goal[i]);
26 }
27 void Mark(int nde,int sze)
28 {
29     siz[nde]=1; int tmp=0;
30     for(int i=p[nde];i;i=noww[i])
31         if(!vis[goal[i]])
32         {
33             Mark(goal[i],sze);
34             siz[nde]+=siz[goal[i]];
35             tmp=max(tmp,siz[goal[i]]);
36         }
37     tmp=max(tmp,sze-siz[nde]);
38     if(tmp<=maxx) maxx=tmp,c=nde;
39 }
40 void DFS(int nde)
41 {
42     stk[++top]=nde;
43     for(int i=p[nde];i;i=noww[i]) 
44         if(!vis[goal[i]]) DFS(goal[i]);
45 }
46 void DP(int nde,int anc)
47 {
48     for(int i=1;i<=top;i++)
49     {
50         int nod=stk[i];
51         while(nde!=fth[anc]&&dis[nod]-dis[nde]<=lim[nod])
52         {
53             while(tpp>=2&&Slope(sta[tpp],nde)>=Slope(sta[tpp-1],sta[tpp])) 
54                 tpp--; sta[++tpp]=nde,nde=fth[nde];
55         }
56         if(tpp)
57         {
58             int l=1,r=tpp,best;
59             while(l<=r)
60             {
61                 int mid=(l+r)/2;
62                 double s=(mid==tpp)?-inf:Slope(sta[mid],sta[mid+1]);
63                 if(s<=pr1[nod]) r=mid-1,best=mid; else l=mid+1;
64             }
65             best=sta[best],dp[nod]=min(dp[nod],dp[best]+(dis[nod]-dis[best])*pr1[nod]+pr2[nod]);
66         }
67     }
68 }
69 void PDC(int nde,int sze)
70 {
71     if(sze<=1) return;
72     maxx=inf,Mark(nde,sze); int hc=c; 
73     for(int i=p[hc];i;i=noww[i])
74         vis[goal[i]]=true,sze-=siz[goal[i]];
75     PDC(nde,sze),top=0;
76     for(int i=p[hc];i;i=noww[i]) DFS(goal[i]);
77     sort(stk+1,stk+1+top,cmp),tpp=0,DP(hc,nde);
78     for(int i=p[hc];i;i=noww[i]) PDC(goal[i],siz[goal[i]]);
79 }
80 int main()
81 {
82     scanf("%d%d",&n,&t);
83     for(int i=2;i<=n;i++)
84     {
85         scanf("%d%lld",&fth[i],&rd),Link(fth[i],i,rd);
86         scanf("%lld%lld%lld",&pr1[i],&pr2[i],&lim[i]),dp[i]=1e18;
87     }
88     Getdis(1),PDC(1,n);
89     for(int i=2;i<=n;i++) printf("%lld
",dp[i]);
90     return 0;
91 }
View Code
原文地址:https://www.cnblogs.com/ydnhaha/p/10219472.html