[BZOJ1758][WC2010]重建计划(点分治+单调队列)

点分治,对于每个分治中心,考虑求出经过它的符合长度条件的链的最大权值和。

从分治中心dfs下去取出所有链,为了防止两条链属于同一个子树,我们一个子树一个子树地处理。

用s1[i]记录目前分治中心伸下去的链中长度为i的链的最大权值,s2[i]记录新子树中的链的最大权值。

分数规划,考虑合并,枚举长度,由于另一个长度在一个滑动窗口中,所以使用单调队列求解即可。

为了保证复杂度,讲子树按高度排序。注意初始化等问题。

 1 #include<cstdio>
 2 #include<vector>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 5 #define For(i,x) for (int i=h[x],k; i; i=nxt[i])
 6 using namespace std;
 7 
 8 const int N=200010;
 9 const double eps=1e-8,inf=1e9;
10 bool vis[N];
11 int n,L,R,u,v,w,S,rt,tot,sz[N],f[N],he[N],d[N],q[N];
12 int cnt,h[N],pre[N],to[N<<1],val[N<<1],nxt[N<<1];
13 double ans,dis[N],s1[N],s2[N];
14 vector<int>ve;
15 void add(int u,int v,int w){ to[++cnt]=v; val[cnt]=w; nxt[cnt]=h[u]; h[u]=cnt; }
16 
17 bool cmp(int a,int b){ return he[a]<he[b]; }
18 
19 void get(int x,int fa){
20     sz[x]=1; f[x]=0;
21     For(i,x) if ((k=to[i])!=fa && !vis[k])
22         get(k,x),f[x]=max(f[x],sz[k]),sz[x]+=sz[k];
23     f[x]=max(f[x],S-sz[x]);
24     if (f[x]<f[rt]) rt=x;
25 }
26 
27 void dfs(int x,int fa){
28     d[x]=d[fa]+1; he[x]=1;
29     For(i,x) if ((k=to[i])!=fa && !vis[k])
30         pre[k]=val[i],dfs(k,x),he[x]=max(he[x],he[k]+1);
31 }
32 
33 void dfs2(int x,int fa,double mid){
34     dis[x]=dis[fa]+pre[x]-mid; s2[d[x]]=max(s2[d[x]],dis[x]);
35     For(i,x) if ((k=to[i])!=fa && !vis[k]) dfs2(k,x,mid);
36 }
37 
38 bool jud(double mid){
39     double res=-inf;
40     rep(i,0,tot){
41         int k=ve[i],st=1,ed=0; dis[rt]=0;
42         rep(j,1,he[k]) s2[j]=-inf; dfs2(k,rt,mid);
43         rep(j,0,he[k]){
44             if (st<=ed && q[st]>R-j) st++;
45             if (L-j<=he[k]){
46                 while (st<=ed && s1[q[ed]]<s1[L-j]) ed--;
47                 q[++ed]=L-j;
48             }
49             if (st<=ed) res=max(res,s1[q[st]]+s2[j]);
50         }
51         rep(j,1,he[k]) s1[j]=max(s1[j],s2[j]);
52     }
53     return res>0;
54 }
55 
56 void solve(int x){
57     vis[x]=1; d[0]=-1; dfs(x,0); dis[x]=0; ve.clear();
58     For(i,x) if (!vis[k=to[i]]) ve.push_back(k=to[i]);
59     tot=ve.size()-1;
60     if (tot==-1) return;
61     sort(ve.begin(),ve.end(),cmp);
62     int ed=he[ve[tot]];
63     double L=ans,R=1e6;
64     while (L+eps<R){
65         double mid=(L+R)/2;
66         rep(i,1,ed) s1[i]=-inf; s1[0]=0;
67         if (jud(mid)) L=mid; else R=mid;
68     }
69     ans=max(ans,L);
70     For(i,x) if (!vis[k=to[i]]) rt=0,S=sz[k],get(k,x),solve(rt);
71 }
72 
73 int main(){
74     freopen("bzoj1758.in","r",stdin);
75     freopen("bzoj1758.out","w",stdout);
76     scanf("%d%d%d",&n,&L,&R);
77     rep(i,2,n) scanf("%d%d%d",&u,&v,&w),add(u,v,w),add(v,u,w);
78     f[0]=n+1; S=n; rt=0; get(1,0);
79     solve(rt); printf("%.3lf\n",ans);
80     return 0;
81 }

 

转载于:https://www.cnblogs.com/HocRiser/p/10176130.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值