题目描述

给一棵树,每条边有权。求一条简单路径,权值和等于 K,且边的数量最小。

题解

比较明显需要用到点分治,我们定义\(d\)数组表示当前节点到根节点\(rt\)之间有多少个节点,也可以表示有多少条边,然后我们在定义\(dis\)表示当前节点到根节点\(rt\)的距离,那么我们就可以得到\(ans=min(ans,d[u]+t[k-dis[u]])\),这个方程还是比较容易的。
但是这道题目不满足状态可减性,说的明白一点就是答案和答案之间无法相减,那么点分治的容斥原理处理就可以变成将原来没有访问过得节点的答案都变成\(inf\),这样我们算的时候就不会把这些重复部分算多次。

ac代码

# include <cstdio>
# include <cstring>
# include <algorithm>
# include <ctype.h>
# include <iostream>
# include <cmath>
# include <map>
# include <vector>
# include <queue>
# define LL long long
# define ms(a,b) memset(a,b,sizeof(a))
# define ri (register int)
# define inf (0x3f3f3f3f)/3
# define pb push_back
# define fi first
# define se second
# define pii pair<int,int>
# define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
using namespace std;
inline int gi(){
    int w=0,x=0;char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return w?-x:x;
}
# define N 1000005
struct edge{
    int to,nt,w;
}E[N<<1];
int H[N],cnt,n,k,rt,sum,mx,sz[N],ans,mxs[N],d[N],dis[N],t[N];
bool vis[N];
void addedge(int u,int v,int w){
    E[++cnt]=(edge){v,H[u],w}; H[u]=cnt;
}
void get_root(int u,int fa){
    sz[u]=1; mxs[u]=-1;
    for (int e=H[u];e;e=E[e].nt){
        if (vis[E[e].to]||E[e].to==fa) continue;
        get_root(E[e].to,u);
        sz[u]+=sz[E[e].to];
        mxs[u]=max(mxs[u],sz[E[e].to]);
    }
    mxs[u]=max(sum-sz[u],mxs[u]);
    if (mxs[u]<mx) mx=mxs[u],rt=u;
}
void calc(int u,int fa){
    if (dis[u]<=k) ans=min(ans,d[u]+t[k-dis[u]]);//如果当前的距离是小于k的,那么就说明可能可以更新答案
    for (int e=H[u];e;e=E[e].nt){
        if (E[e].to==fa||vis[E[e].to]) continue;
        d[E[e].to]=d[u]+1;
        dis[E[e].to]=dis[u]+E[e].w;
        calc(E[e].to,u);
    }
}
void update(int u,int fa,bool fg){//更新边上的答案
    if (dis[u]<=k) {
        if (fg) t[dis[u]]=min(t[dis[u]],d[u]);//如果是访问过得,那么就直接更新
        else t[dis[u]]=inf;//如果没有访问过,那么就赋值成inf,防止重复
    }
    for (int e=H[u];e;e=E[e].nt){//继续更新边上的答案
        if (E[e].to==fa||vis[E[e].to]) continue;
        update(E[e].to,u,fg);
    }
}
void divide(int u){
    vis[u]=1; t[0]=0;
    for (int e=H[u];e;e=E[e].nt){
        if (vis[E[e].to]) continue;
        d[E[e].to]=1; dis[E[e].to]=E[e].w; calc(E[e].to,-1); update(E[e].to,-1,1);//计算全局的答案
    }
    for (int e=H[u];e;e=E[e].nt){//将所有没有访问过得答案还原,防止答案重复
        if (!vis[E[e].to]) update(E[e].to,-1,0);
    }
    for (int e=H[u];e;e=E[e].nt){//递归分治
        if (vis[E[e].to]) continue;
        mx=inf; sum=sz[E[e].to]; rt=0; get_root(E[e].to,-1); divide(rt);
    }
}
int main(){
    ms(t,inf); n=gi(),k=gi();
    for (int i=2;i<=n;i++){
        int u=gi(),v=gi(),w=gi();
        u++,v++;
        addedge(u,v,w); addedge(v,u,w);
    }
    sum=n; mx=inf; rt=-1; get_root(1,-1);
    ans=inf; divide(rt);
    printf("%d\n",(ans==inf)?(-1):(ans));
    return 0;
}

[luogu4149][bzoj2599][IOI2011]Race【点分治】的更多相关文章

  1. [bzoj2599][IOI2011]Race——点分治

    Brief Description 给定一棵带权树,你需要找到一个点对,他们之间的距离为k,且路径中间的边的个数最少. Algorithm Analyse 我们考虑点分治. 对于子树,我们递归处理,所 ...

  2. BZOJ2599:[IOI2011]Race(点分治)

    Description 给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000 Input 第一行 两个整数 n, k 第二 ...

  3. 【BZOJ-2599】Race 点分治

    2599: [IOI2011]Race Time Limit: 70 Sec  Memory Limit: 128 MBSubmit: 2590  Solved: 769[Submit][Status ...

  4. BZOJ 2599: [IOI2011]Race( 点分治 )

    数据范围是N:20w, K100w. 点分治, 我们只需考虑经过当前树根的方案. K最大只有100w, 直接开个数组CNT[x]表示与当前树根距离为x的最少边数, 然后就可以对根的子树依次dfs并更新 ...

  5. [IOI2011]Race 点分治

    [IOI2011]Race LG传送门 点分治板子题. 直接点分治统计,统计的时候开个桶维护下就好了. 注(tiao)意(le)细(hen)节(jiu). #include<cstdio> ...

  6. bzoj2599/luogu4149 [IOI2011]Race (点分治)

    点分治.WA了一万年. 重点就是统计答案的方法 做法一(洛谷AC bzojWA 自测WA): 做点x时记到x距离为k的边数最小值为dis[k],然后对每一对有值的dis[i]和dis[K-i],给an ...

  7. bzoj2599: [IOI2011]Race(点分治)

    写了四五道点分治的题目了,算是比较理解点分治是什么东西了吧= = 点分治主要用来解决点对之间的问题的,比如距离为不大于K的点有多少对. 这道题要求距离等于K的点对中连接两点的最小边数. 那么其实道理是 ...

  8. 2019.01.09 bzoj2599: [IOI2011]Race(点分治)

    传送门 题意:给一棵树,每条边有权.求一条路径,权值和等于K,且边的数量最小. 思路: 考虑点分治如何合并. 我们利用树形dpdpdp求树的直径的方法,边dfsdfsdfs子树边统计答案即可. 代码: ...

  9. BZOJ2599 [IOI2011]Race 【点分治】

    题目 给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000 输入格式 第一行 两个整数 n, k 第二..n行 每行三个整 ...

随机推荐

  1. LZO

    LZO 是致力于解压速度的一种数据压缩算法,LZO 是 Lempel-Ziv-Oberhumer 的缩写.这个算法是无损算法,参考实现程序是线程安全的. 实现它的一个自由软件工具是lzop.最初的库是 ...

  2. Git命令以及常见注意事项

    命令: git init -> 初始化一个git仓库 git clone -> 克隆一个本地库 git pull -> 拉取服务器最新代码 git fetch –p -> 强行 ...

  3. [转帖] SS, SP, BP 三个寄存器

    SS, SP, BP 三个寄存器 https://blog.csdn.net/vspiders/article/details/55669265 这么看 计算机组成原理 还有 考试的很多题目非常有用啊 ...

  4. Redis 安装学习

    Linux下下载安装redis https://redis.io/download tar -zvxf redisxxx cd redisxxxx make  ---进行安装 vim ~.bash_p ...

  5. 使用cmd命令删除文件夹下所有文件

    rmdir 删除整个目录 好比说我要删除 222 这个目录下的所有目录和档案,这语法就是: rmdir /s/q 222 其中: /s 是代表删除所有子目录跟其中的档案. /q 是不要它在删除档案或目 ...

  6. js中的call、apply、bind

    在js中每个函数都包含两个非继承而来的方法:call()和apply() call和apply的作用都是在特定的作用域中将函数绑定到另外一个对象上去运行,即可以用来重新定义函数的执行环境,两者仅在定义 ...

  7. 随机森林(Random Forest)

    阅读目录 1 什么是随机森林? 2 随机森林的特点 3 随机森林的相关基础知识 4 随机森林的生成 5 袋外错误率(oob error) 6 随机森林工作原理解释的一个简单例子 7 随机森林的Pyth ...

  8. react 入坑笔记(二) - State

    React State 一. state 大致思想:在 react 中,每个组件都是一个状态机,通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致.React 里,只需更新组件的 ...

  9. caffe boost cuda __float128 undefined

    转载:https://blog.csdn.net/thesby/article/details/50512886 编译caffe-master时遇到的问题,__float128未定义,使用到cuda版 ...

  10. codeforces362B

    Petya and Staircases CodeForces - 362B 题意: 一个小男孩要上楼梯,他一次可以走1个台阶或2个台阶或3个台阶,但是有一些台阶是脏的,他不想走在脏台阶上.一共有n个 ...