[WC2010]重建计划(长链剖分版)

传送门

Description

Solution

时隔多年,补上了这题的长链剖分写法

感觉比点分治要好写的多

我们假设\(pos\)是当前点的\(dfn\),它距离所在链的底端的边的数量是\(len\),距离是\(siz\)

那么我们要求得\(g[pos...pos+len]\)

其中\(g[pos+i]+siz\)表示的是当前点往下长度为\(i\)的最长链的大小

初始情况下,\(g[pos]=-siz[pos]\)

为什么要这么写呢?

因为转移重儿子的时候,我们直接把数组右移了一位,这样子定义使得原先的值仍然有意义

考虑如何转移?

对于一个轻儿子,\(dfn\)为\(pv\)

那么\(当前点,轻儿子g[pos+j+1]\leftarrow siz[pv]+g[pv+j]+w(当前点,轻儿子)-siz\)

考虑如何更新答案

对于\(lca\)为当前点的链

  1. 首先计算一个端点就是当前点的:\((g[pos+i])_{min}+siz\)
  2. 然后是两个端点分别处于不同子树的

    枚举一个轻儿子子树内的链长:\(当前点,轻儿子(g[pos+i])_{min}+siz+(siz[pv]+g[pv+j])+(w(当前点,轻儿子))\)

求min的部分用线段树优化

这题卡一定的常数,不知道为什么,std::max/min要比手写的快

Code?

#include<bits/stdc++.h>
#define ll long long
#define reg register
#define ri reg int i
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
const int MN=1e5+5;
const double eps=1e-4,inf=0x3f3f3f3f;
double siz[MN],g[MN],ans,mid;
struct edge{int to,w,nex;}e[MN<<1];int hr[MN],en;
inline void ins(int x,int y,int w)
{
    e[++en]=(edge){y,w,hr[x]};hr[x]=en;
    e[++en]=(edge){x,w,hr[y]};hr[y]=en;
}
int dfn[MN],len[MN],mx[MN],w[MN],ind,n,L,R;
struct SegmentTree
{
    #define ls (x<<1)
    #define rs (x<<1|1)
    #define Mid ((l+r)>>1)
    double t[MN<<2];
    inline void clear(){for(ri=0;i<(MN<<2);++i)t[i]=-inf;}
    void Modify(int x,int l,int r,int a,double val)
    {
        if(l==r) return(void)(t[x]=max(t[x],val));
        if(a<=Mid) Modify(ls,l,Mid,a,val);
        else Modify(rs,Mid+1,r,a,val);
        t[x]=max(t[ls],t[rs]);
    }
    double Query(int x,int l,int r,int a,int b)
    {
        if(l==a&&r==b) return t[x];if(a>b)return -inf;
        if(b<=Mid) return Query(ls,l,Mid,a,b);
        if(a>Mid) return Query(rs,Mid+1,r,a,b);
        return max(Query(ls,l,Mid,a,Mid),Query(rs,Mid+1,r,Mid+1,b));
    }
}T;
void dfs(int x,int f=0)
{
    for(ri=hr[x];i;i=e[i].nex)if(e[i].to^f)
    {
        dfs(e[i].to,x);
        if(len[e[i].to]>=len[mx[x]]) mx[x]=e[i].to,w[x]=e[i].w;
        if(len[e[i].to]+1>len[x]) len[x]=len[e[i].to]+1;
    }
}
void solve(int x,int f=0)
{
    if(!dfn[x]) dfn[x]=++ind;
    reg int i,j,pos=dfn[x];
    if(mx[x]) solve(mx[x],x),siz[pos]=siz[pos+1]+w[x]-mid;
    T.Modify(1,1,n,pos,g[pos]=-siz[pos]);
    if(L<=len[x])
    {
        double tmp=T.Query(1,1,n,pos+L,pos+min(len[x],R));
        ans=max(ans,tmp+siz[pos]);
    }
    for(i=hr[x];i;i=e[i].nex)if(e[i].to!=f&&e[i].to!=mx[x])
    {
        solve(e[i].to,x);reg int pv=dfn[e[i].to];
        for(j=0;j<=len[e[i].to];++j)
        {
            double tmp=T.Query(1,1,n,pos+max(0,L-j-1),pos+min(len[x],R-j-1));
            ans=max(ans,tmp+siz[pv]+siz[pos]+g[pv+j]+e[i].w-mid);
        }
        for(j=0;j<=len[e[i].to];++j)
        {
            double tmp=siz[pv]+g[pv+j]+e[i].w-mid-siz[pos];
            if(tmp>g[pos+j+1]) T.Modify(1,1,n,pos+j+1,g[pos+j+1]=tmp);
        }
    }
}
bool check(){T.clear();ans=-inf;solve(1);return ans>=eps;}
int main()
{
    n=read();L=read();R=read();
    reg int i,x,y;
    for(i=1;i<n;++i) x=read(),y=read(),ins(x,y,read());
    dfs(1);double l=0.,r=1e6;
    for(i=1;i<=40;++i)
    {
        if(l+eps>=r) break;
        mid=(l+r)/2.;
        if(check()) l=mid;else r=mid;
    }
    printf("%.3lf",l);
    return 0;
}


Blog来自PaperCloud,未经允许,请勿转载,TKS!

原文地址:https://www.cnblogs.com/PaperCloud/p/10662441.html

时间: 2024-10-04 13:37:47

[WC2010]重建计划(长链剖分版)的相关文章

长链剖分随想

之前写了那么长一篇Blog-现在不如写篇小短文-说一下另一种树链剖分方法--长链剖分的事情.它可以比重链剖分更快地完成一些东西. 树链剖分的原始版本重链剖分非常经典,这里就不从头介绍了. 原本的剖分方法是按照子树大小剖分,与子树点数最多的儿子连成链,所以叫做重链剖分-然后显然就有一个点到根的路径上至多$O(\log n)$条轻边这个性质(因为沿着轻边走,每次子树大小一定小于父亲的一半).有了这个性质就可以做各种路径相关的查询,暴力每次跳到重链开头就好- 而在一些问题里,有这么一种奇妙的剖分方式可

【BZOJ 1758】 [Wc2010]重建计划

1758: [Wc2010]重建计划 Time Limit: 40 Sec  Memory Limit: 162 MB Submit: 989  Solved: 345 [Submit][Status] Description Input 第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数Ai,Bi,Vi分别表示道路(Ai,Bi),其价值为Vi 其中城市由1..N进行标

bzoj 1758 [Wc2010]重建计划 分数规划+树分治单调队列check

[Wc2010]重建计划 Time Limit: 40 Sec  Memory Limit: 162 MBSubmit: 4345  Solved: 1054[Submit][Status][Discuss] Description Input 第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数Ai,Bi,Vi分别表示道路(Ai,Bi),其价值为Vi 其中城市由1..N

【CF860E】Arkady and a Nobody-men 长链剖分

[CF860E]Arkady and a Nobody-men 题意:给你一棵n个点的有根树.如果b是a的祖先,定义$r(a,b)$为b的子树中深度小于等于a的深度的点的个数(包括a).定义$z(a)=\sum\limits r(a,b)$(b是a的祖先).要你求出每个点的z值. $n\le 5\times 10^5$ 题解:一开始naive的思路:将所有点按深度排序,将深度相同的点统一处理,统计答案时相当于链加,链求和,用树剖+树状数组搞一搞,时间复杂度$O(n\log^2n)$. 后来看题解

【BZOJ3522】【BZOJ4543】【POI2014】Hotel 树形DP 长链剖分 启发式合并

题目大意 ? 给你一棵树,求有多少个组点满足\(x\neq y,x\neq z,y\neq z,dist_{x,y}=dist_{x,z}=dist_{y,z}\) ? \(1\leq n\leq 100000\) 题解 ? 问题转换为有多少个组点满足\(dist_{i,x}=dist_{i,y}=dist_{i,z}\) ? 我们考虑树形DP ? \(f_{i,j}=\)以\(i\)为根的子树中与\(i\)的距离为\(j\)的节点数 ? \(g_{i,j}=\)以\(i\)为根的子树外选择一个

【CF1009F】 Dominant Indices (长链剖分)

题目链接 \(O(n^2)\)的\(DP\)很容易想,\(f[u][i]\)表示在\(u\)的子树中距离\(u\)为\(i\)的点的个数,则\(f[u][i]=\sum f[v][i-1]\) 长链剖分. \(O(1)\)继承重儿子的信息,再暴力合并其他轻儿子的信息,时间复杂度是线性的. 继承重儿子用指针实现,非常巧妙. #include <cstdio> int xjc; char ch; inline int read(){ xjc = 0; ch = getchar(); while(c

P4292 [WC2010]重建计划

传送门 首先这玩意儿很明显是分数规划,二分一个答案\(mid\),边权变为\(w_i-mid\),然后看看能不能找到一条路径长度在\([L,R]\)之间,且边权总和非负,这个可以转化为求一条满足条件的边权最大的路径 这个实际上可以用点分做,用单调队列可以优化到\(O(nlog^2n)\),然而我不知道为什么写挂掉了所以这里就不说了-- 我们设\(f_{i,j}\)表示以\(i\)为根的子树中,从\(i\)向下走\(j\)步的链最长是多少.转移可以用长链剖分优化到均摊\(O(1)\).查询答案的话

长链剖分总结

长链剖分总结 概念 长链剖分和轻重链剖分十分相似,都是将一棵树节点的信息分成多条链的信息,但是前者是以深度剖分,后者则是以子树大小来剖分. 同时长链剖分还借鉴了$dsu\;on\;tree$的一些$trick$使得它能十分高效地合并子树信息. 性质 破天荒地写了证明 性质一 所有链长度之和为节点数 证明: 每个点在且仅在一条链中 性质二 任意一个点$k$级祖先所在长链的长度一定大于等于$k$ 证明: 假如$y$所在长链的长度小于$k$,那么它所在的链一定不是重链,因为$x-y$这条链显然更优,那

The 2019 ICPC China Nanchang National Invitational and International Silk-Road Programming Contest E. Interesting Trip 长链剖分

题库链接 考虑莫比乌斯, 套上去之后就是变成了统计长度为d的一共有多少路径, 直接长链剖分, 在计蒜客上极度卡常, 卡了一万年才卡过去, 现场好像还有用点分治过去的, 这都能过?? #include<bits/stdc++.h> #define LL long long using namespace std; const int N = (int)5e5 + 7; const int M = 30000; int n, d, a[N], vis[N], miu[M + 1]; int now