【十一联考 1】T2 树链剖分

算法思路就不说了,其实比较简单

这里讲一下坑点:

1.虽然我们可以将边权压到点上,但是当根不同时,差分数组显然不同。所以我们不能真的将边权看做点权,换根时还是要将其当做边权来考虑。
1.更新时,我们设dp[u]表示u节点为根时的最大价值,由于经过点u后,点v的儿子将变成点u,所以此时我们要更新点v的儿子中的最大值以及次大值。注意!不止要更新最大值,次大值也要正确维护

我的代码没有加优化,因为保证正确就不错了

#include<bits/stdc++.h>
using namespace std;

#define go(i,a,b) for(int i=a;i<=b;++i)
#define com(i,a,b) for(int i=a;i>=b;--i)
#define mem(a,b) memset(a,b,sizeof(a))
#define head(x) for(int i=head[x];i+1;i=e[i].nxt)
#define int long long

const int inf=0x3f3f3f3f3f3f3f3f,N=1e5+10,t=17;

int n,m,head[N],cnt,son[N],c[N],dep[N],f[N][20],mx[N],se[N],ans=0,dp[N];

struct edge{
    int nxt,v;
}e[N*2];

void add(int u,int v){
    e[cnt]=(edge){head[u],v};
    head[u]=cnt++;
}

inline void read(int &x){
    x=0;char f=1,c=getchar();
    while(!isdigit(c)){ if(c=='-') f=-1; c=getchar(); }
    while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); }
    x*=f;
}

void dfs(int u,int fa){
    dep[u]=dep[fa]+1;
    f[u][0]=fa;
    head(u){
        int v=e[i].v;
        if(v==fa) continue;
        dfs(v,u);
    }
}

int lca(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    com(i,t,0){
        if(dep[f[x][i]]>=dep[y]) x=f[x][i];
    }
    if(x==y) return x;
    com(i,t,0){
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    }
    return f[x][0];
}

void Dfs(int u,int fa){
    head(u){
        int v=e[i].v;
        if(v==fa) continue;
        Dfs(v,u);
        c[u]+=c[v];
    }
}

void DFS(int u,int fa){
    head(u){
        int v=e[i].v;
        if(v==fa) continue;
        DFS(v,u);
        if(c[v]>=mx[u]){
            se[u]=mx[u];
            mx[u]=c[v];
        }
        else if(c[v]>se[u]) se[u]=c[v];
    }
    dp[1]+=mx[u];
}

void DFs(int u,int fa){
    head(u){
        int v=e[i].v;
        if(v==fa) continue;
        dp[v]=dp[u];
        if(mx[u]==c[v]) dp[v]=dp[v]-mx[u]+se[u];
        if(c[v]>=mx[v]){
            dp[v]=dp[v]+c[v]-mx[v];
            se[v]=mx[v];
            mx[v]=c[v];
        }
        else if(c[v]>se[v]) se[v]=c[v];
        DFs(v,u);
    }
}

signed main(){
    mem(head,-1);
    read(n),read(m);
    int u,v,Lca;
    go(i,2,n){
        read(u),read(v);
        add(u,v),add(v,u);
    }
    dfs(1,0);
    go(j,1,t)
        go(i,1,n)
            f[i][j]=f[f[i][j-1]][j-1];
    while(m--){
        read(u),read(v);
        Lca=lca(u,v);
        ++c[u],++c[v];
        c[Lca]-=2;
        ans+=dep[u]+dep[v]-2*dep[Lca];
    }
    Dfs(1,0);
    DFS(1,0);
    DFs(1,0);
    int tot=ans;
    go(i,1,n)
        ans=min(ans,tot-dp[i]);
    printf("%lld",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/White-star/p/11616222.html

时间: 2024-08-24 00:38:02

【十一联考 1】T2 树链剖分的相关文章

BZOJ 4034: [HAOI2015]T2 树链剖分

4034: [HAOI2015]T2 Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. Input 第一行包含两个整数 N, M .表示点数和操作数. 接下来一行 N 个整数,表示树中节点的初始权值. 接下来 N-1 行每行三个正整数 fr, to , 表示该树

BZOJ 4034 [HAOI2015]T2 树链剖分+线段树

题意: 一棵以1为根的树,有n个节点,m个操作. 第一种单点修改. 第二种修改一个点的子树. 第三种询问一个点到根的路径上所有点的权值和. 解析: 看到有人在做我就跑过来看了一下,看完题发现这不SB题么- - 于是就写了下,差点被出题人气死. TMD 那个 fr , to 难道就是逗我玩的? 你丫fr,to不代表有向边? 这么出题不会掉RP? 改了20分钟就这错了?你逗我? 第一种操作略 第二种操作修改子树-dfs序. 第三种链剖完之后直接找就行了. 复杂度O(nlog^2n); 代码: #in

JZYZOJ1539[haoi2015]T2 树链剖分

http://172.20.6.3/Problem_Show.asp?id=1539 在学校的OJ又写了一次,RE了好多次,原来haoi的时候这道题需要开栈+快读,裸数据结构30分,加上快读50分.oi考试的时候原来不能汇编开栈,只能写手工栈orz,学长说当时省选最高分50,本来以为很简单的题没想到这么套路. 代码 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algo

BZOJ 1969: [Ahoi2005]LANE 航线规划( 树链剖分 )

首先我们要时光倒流, 倒着做, 变成加边操作维护关键边. 先随意搞出一颗树, 树上每条边都是关键边(因为是树, 去掉就不连通了)....然后加边(u, v)时, 路径(u, v)上的所有边都变成非关键边了, 因为形成了环, 环上任意2点有2条路径...下图, 加上蓝色的边, 红色x的边就变成了非关键边. 所以树链剖分维护一下...时间复杂度O(NlogN+MlogM+Qlog^2N), 可以AC. 翻了翻ZY的标解:“动态维护树+最近公共祖先查询”....复杂度是O(NlogN+M+QlogN)

【BZOJ1036】树的统计Count(树链剖分)

题意:一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I II. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 1<=n<=30000,0<=q<=200000:中途操作中保证每个节点的权值w在-30000到30000之间. 思路:树链

hdu 5893 (树链剖分+合并)

List wants to travel Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 429    Accepted Submission(s): 92 Problem Description A boy named List who is perfect in English. Now he wants to travel an

bzoj 2243 染色(树链剖分)

题外话 首先这是个挺裸的题,由于太久没写剖分导致调了好久,前天调了一下午,一直查不到错 昨晚在看春晚的时候突然灵机一动,发现合并的时候出了问题,开电脑把它A掉了= = 感觉自己也蛮拼的 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1:将节点a到节点b路径上所有点都染成颜色c 2:询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如"112221"由3段组成:"11"."222"和"1&q

SPOJ375.QTREE树链剖分

题意:一个树,a b c 代表a--b边的权值为c.CHANGE x y  把输入的第x条边的权值改为y,QUERY x y 查询x--y路径上边的权值的最大值. 第一次写树链剖分,其实树链剖分只能说是一种思想.树链剖分  就是 先选择从根节点到叶子节点的最长的路径的权值对应到线段树上,然后从一个子树的根节点到叶子的最长路径的权值对应到线段树上这样直到把所有的点都处理了,然后就是线段树区间查询最值了. 具体可以看这个博客.http://blog.sina.com.cn/s/blog_6974c8

bzoj4304 (树链剖分+线段树)

Problem T2 (bzoj4304 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 解题分析 练手题.树链剖分+线段树. 参考程序 1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #incl