树形结构升级

(以下默认 \(n\le 10^5\)

C Codeforces 191C

给你一棵树,有 \(k\) 对节点,问每条边有多少对节点的路径经过。

树上差分搞一搞。(从下往上)

D BZOJ 3124

求一棵树直径长度、有多少条边满足所有的直径都经过该边。

求直径好办。
第二问中,在直径上按顺序访问(我们假设是从左到右)每一个节点做一遍dfs,如果搜到某个节点到当前节点的距离等于当前节点到直径 左 端点的距离,那么该节点左边的所有边一定不满足题意。
如果搜到某个节点到当前节点的距离等于当前节点到直径 右 端点的距离,那么break;

E Codeforces 734E

一棵树,每个节点有黑、白两色。
现在可以进行若干次操作,每次选定一个节点,可以将所有满足条件的节点涂成与该节点相同的颜色:选定的节点与该节点的路径上所有节点的颜色都与该节点相同。
问最少几步能将整棵树涂成同一种颜色。

初始时和每次操作后将颜色相同且相邻的节点缩成一点(需使用并查集),然后建一棵缩点后的新树,然后求出该树的直径,答案为 \((直径+1)/2\)
方法是不停地更改一个节点的颜色。

F Codeforces 379F

给你一棵树,初始时长这样
1516397-20190303153419642-1888080657.png

然后进行 \(n\) 次操作,每次选定一个节点,在它下面增加两个子节点,并询问当前树的直径。

每增加一个节点就检查它到直径两个端点的距离是否大于当前直径长度,如果大于,更新当前直径的一个端点。
两节点的距离用lca解决。

G Codeforces 294E

有一棵树,现在你要删除一条边,然后增加一条边权与删除的边的边权相等的边,需保证树依然连通,并最小化两两节点的距离和。输出这个距离和。
\((2 ≤ n ≤ 5000)\)

先枚举每一条边作为删除的边,然后计算答案。
新建的边一定建在删边之后形成的两颗子树的重心之间。
然后运用加法原理+乘法原理dp一下。(有点复杂)(注意别忘开long long

Code

#include<bits/stdc++.h>
#define maxn 5003
#define INF 100000000000000000ll
using namespace std;
struct edge{
    int to,next,w;
}e[maxn<<1];
int head[maxn],cnte,dep[maxn];
void add(int u,int v,int w){
    e[++cnte].to=v;
    e[cnte].w=w;
    e[cnte].next=head[u];
    head[u]=cnte;
}
int n,G,sz[maxn];
long long sumw[maxn],answ[maxn],sumG;
void solve(int u,int last){
    sz[u]=1;
    sumw[u]=0;
    answ[u]=0;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==last)continue;
        solve(v,u);
        answ[u]+=answ[v]+sumw[v]*sz[u]+sumw[u]*sz[v]+(long long)sz[u]*sz[v]*e[i].w;
        sumw[u]+=sumw[v]+(long long)e[i].w*sz[v];
        sz[u]+=sz[v];
    }
}
void dfs(int u,int last,int totsz){
    sumG=min(sumG,sumw[u]);
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==last)continue;
        sumw[v]=sumw[u]+(long long)(totsz-sz[v]-sz[v])*e[i].w;
        dfs(v,u,totsz);
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
        add(v,u,w);
    }
    long long ans=INF;
    for(int u=1;u<=n;u++){
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to;
            solve(u,v);
            sumG=INF;
            dfs(u,v,sz[u]);
            long long res=(n-sz[u])*sumG+answ[u];
            solve(v,u);
            sumG=INF;
            dfs(v,u,sz[v]);
            res+=sz[u]*sumG+answ[v]+(long long)sz[u]*sz[v]*e[i].w;
            ans=min(ans,res);
        }
    }
    cout<<ans<<endl;
    return 0;
}

H BZOJ 1977

求一张无向图的严格次小生成树的边权和。

先用Kruskal求出最小生成树(MST)。
然后对于每一条未加入的边,算出该边的两个节点在MST中路径上的最大值和次大值(需要用到倍增及其预处理)。
可以证明,这个最大值一定 \(\le\) 该边边权。
如果最大值等于边权,那么用边权更新次大值;否则用边权更新最大值。

I ZOJ 3649

有一颗树,有点权,记为 \(a_i\) ,有若干询问,每个询问有 \(u,v\) 两个数,问在 \(u,v\) 之间的节点路径序列上,最大的 \(a_k-a_j(c_k≥c_j,j\le k)\) 是多少。

倍增处理四个值:(以下 \(dep\) 为深度数组)
\(A_u=\max\{a_i\}(i在路径(u,1)上)\)
\(B_u=\min\{a_i\}(i在路径(u,1)上)\)
\(C_u=\max\{a_i-a_j\}(i,j在路径(u,1)上,dep[i]<dep[j])\)
\(D_u=\max\{a_i-a_j\}(i,j在路径(u,1)上,dep[i]>dep[j])\)

每次查询两个节点 \(x,y\) 时,我们令 \(x\) 所在的链为左链, \(y\) 所在的链为右链,答案为\(\max\{C_x,D_y,A_y-B_x,A_j-B_i,A_l-B_k\}(i,j在左链上,dep[j]<dep[i];k,l在右链上,dep[l]>dep[k])\)

J hdu 4582

给你一张图及其dfs生成树,问最少选择多少条边,使得图中所有的简单环(只有一条边不在dfs生成树内)至少包含一条已选择的边。

当是一条链的情况时,对所有右端点排序,然后每次贪心选最左边的右端点所在的边。
当时一棵树的情况时,按深度从大到小排序即可。

K poj 1144

给你一张无向图,求割边数量。

模板题

L ZOJ 2588

给你一张无向图,输出所有割边。

同上

M hdu 1007

求平面上的最近点对。

分治。
按横坐标将所有节点排序,每次取一个mid,然后左、右区间分治,合并时统计分治上来的答案 \(d\)
我们做一条直线 \(x=x_mid\) ,然后找出在直线 \(x=x_mid-d\)\(x=x_mid+d\) 中间的点,暴力统计这些点的答案。
算法导论中有证明,刚刚的这些点个数不会超过 \(8\) 个。

N hdu 4822

给定一棵树,和树上节点 \(A,B,C\) ,若节点 \(X\) 到节点 \(A\) 的距离严格小于到 \(B,C\) 的距离,那么称 \(X\)\(A\) 占有。有若干询问,每次给定一组 \(A,B,C\) ,问各占领的节点数。

\(\color{white}{码力过大~~~}\)
https://www.cnblogs.com/oyking/p/3916442.html

转载于:https://www.cnblogs.com/BlogOfchc1234567890/p/10466112.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值