2599: [IOI2011]Race

Time Limit: 70 Sec  Memory Limit: 128 MB
http://www.lydsy.com/JudgeOnline/problem.php?id=2599

Description

给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000

Input

第一行 两个整数 n, k
第二..n行 每行三个整数 表示一条无向边的两端和权值 (注意点的编号从0开始)

Output

一个整数 表示最小边数量 如果不存在这样的路径 输出-1

Sample Input

4 3
0 1 1
1 2 2
1 3 4

Sample Output

2
点分治
是求两点间边权和<=k的数量
这里是求两点间边权和=k的最少边数
我们仍然可以借用上题的方法,多记录一个节点到根节点的经过的边数即可
有2个地方需要修改:
① 上题需要减去同一子树中不合法的点对个数
    本题虽不需要考虑同一子树内的情况,但需要在计算时跳过同一子树内的2个点
    具体做法是 在递归记录点与根节点间权值和、边数时,顺带记录每个点属于 当前根节点的哪颗子树
     计算时,如果属于同一子树,跳过
    代码:
  if(fa==head||!fa) deep[deep[0].edge_sum].id=x;

  else deep[deep[0].edge_sum].id=deep[deep[0].edge_sum-1].id;

当前点为x,head表示当前根节点下的哪颗子树,fa表示x的父节点,id记录当前点属于哪颗子树

上题能不能采用同样的方法,避免计算子树内部的情况呢?

不能。因为排序仅按边权大小排,累计答案的方式是加r-l,即一堆满足条件的点判断一次,一起累加。

判断的两个点在同一子树内,其他的点可能不在同一子树内

②统计答案的时候,仍然可以同上题一样采用两边指针向中间逼近的方式

但要特殊处理指针指向位置周围边权相等的情况

 

#include<cstdio>
#include<algorithm>
#define N 200001
using namespace std;
int n,k,tot,sum,root,son[N],f[N],d[N],ans=N;
int front[N],to[N*],next[N*],w[N*];
bool v[N];
struct node
{
int dis,edge_sum,id;
}deep[N];
void getroot(int x,int fa)
{
son[x]=;f[x]=;
for(int i=front[x];i;i=next[i])
{
if(to[i]==fa||v[to[i]]) continue;
getroot(to[i],x);
son[x]+=son[to[i]];
f[x]=max(f[x],son[to[i]]);
}
f[x]=max(f[x],sum-son[x]);
if(f[x]<f[root]) root=x;
}
void getdeep(int head,int x,int fa,int edge_sum)
{
deep[++deep[].edge_sum]=(node){d[x],edge_sum};
if(fa==head||!fa) deep[deep[].edge_sum].id=x;
else deep[deep[].edge_sum].id=deep[deep[].edge_sum-].id;
for(int i=front[x];i;i=next[i])
{
if(v[to[i]]||to[i]==fa) continue;
d[to[i]]=d[x]+w[i];
getdeep(head,to[i],x,edge_sum+);
}
}
bool cmp(node l,node r)
{
/*if(l.dis!=r.dis) return l.dis<r.dis;
return l.edge_sum>r.edge_sum;*/
return l.dis<r.dis;
}
void cal(int x,int now)
{
d[x]=now;deep[].edge_sum=;
getdeep(x,x,,);
int l=,r=deep[].edge_sum,t=;
sort(deep+,deep+r+,cmp);
while(l<r)
{
/*if(deep[l].dis+deep[r].dis==k&&deep[l].id!=deep[r].id)
{
ans=min(ans,deep[l].edge_sum+deep[r].edge_sum);
//printf("%d %d\n",deep[l].dis,deep[r].dis);
l++; }*/ // 错误的
if(deep[l].dis+deep[r].dis==k)
{
int p1=l,p2=r;
while(deep[p1].dis+deep[r].dis==k) p1++;p1--;
while(deep[p2].dis+deep[l].dis==k) p2--;p2++;
for(int i=l;i<=p1;i++)
for(int j=p2;j<=r;j++)
if(deep[i].id!=deep[j].id)
ans=min(ans,deep[i].edge_sum+deep[j].edge_sum);
l=p1+;r=p2-;
}
else if(deep[l].dis+deep[r].dis<k) l++;
else r--;
}
}
void work(int x)
{
cal(x,);
v[x]=true;
for(int i=front[x];i;i=next[i])
{
if(v[to[i]]) continue;
sum=son[to[i]];
root=;
getroot(to[i],root);
work(root);
}
}
void add(int u,int v,int val)
{
to[++tot]=v;next[tot]=front[u];front[u]=tot;w[tot]=val;
to[++tot]=u;next[tot]=front[v];front[v]=tot;w[tot]=val;
}
int read()
{
int x=,f=;char c=getchar();
while(c<''||c>'') {if(c=='-') f=-;c=getchar();}
while(c>=''&&c<='') {x=x*+c-'';c=getchar();}
return x*f;
}
int main()
{
n=read();k=read();
int x,y,z;
for(int i=;i<n;i++)
{
x=read();y=read();z=read();
x++;y++;
add(x,y,z);
}
f[]=N;
sum=n;
getroot(,);
work(root);
printf("%d",ans==N ? -:ans);
}

[IOI2011]Race的更多相关文章

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

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

  2. 【BZOJ2599】[IOI2011]Race 树的点分治

    [BZOJ2599][IOI2011]Race Description 给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 100000 ...

  3. [IOI2011]Race 点分治

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

  4. bzoj 2599 [IOI2011]Race 点分

    [IOI2011]Race Time Limit: 70 Sec  Memory Limit: 128 MBSubmit: 4768  Solved: 1393[Submit][Status][Dis ...

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

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

  6. 2599: [IOI2011]Race

    2599: [IOI2011]Race 链接 分析 被memset卡... 点分治,对于重心,遍历子树,记录一个数组T[i],表示以重心为起点的长度为i的路径中最少的边数是多少.然后先遍历子树,更新答 ...

  7. 模板—点分治B(合并子树)(洛谷P4149 [IOI2011]Race)

    洛谷P4149 [IOI2011]Race 点分治作用(目前只知道这个): 求一棵树上满足条件的节点二元组(u,v)个数,比较典型的是求dis(u,v)(dis表示距离)满足条件的(u,v)个数. 算 ...

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

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

  9. BZOJ2599 [IOI2011]Race

    传送门 点分治,黄学长的选根方法会T掉,换了这个人的选根方法就可以了. 当然,你也可以选择黄学长的奇淫优化 //BZOJ 2599 //by Cydiater //2016.9.23 #include ...

随机推荐

  1. 四则运算结对项目之GUI

    本次结对编程让我学到了许多许多知识,受益匪浅!在此之前,我没想过我能做出一个双击运行的小程序. 感谢我的队友与我同心协力,感谢室友宇欣告诉我操作符为“最多多少”而不是“多少”并教我使用效能分析工具,感 ...

  2. 深入理解JAVA I/O系列三:字符流详解

    字符流为何存在 既然字节流提供了能够处理任何类型的输入/输出操作的功能,那为什么还要存在字符流呢?容我慢慢道来,字节流不能直接操作Unicode字符,因为一个字符有两个字节,字节流一次只能操作一个字节 ...

  3. 蜗牛慢慢爬 LeetCode 9. Palindrome Number [Difficulty: Easy]

    题目 Determine whether an integer is a palindrome. Do this without extra space. Some hints: Could nega ...

  4. svm小问题

    1.没有报错但是结果是pedicttestlabel = [] accuracy = [] 举例:(前提是装了工具箱libsvm-3.21) data=[178,80;172,75;160,50;15 ...

  5. android自动化之appium的环境搭建

    简介appium     appium是C/S架构,appium的核心是一个web服务器,它提供了一套REST的接口,他会接收客户端的连接,监听到命令.执行会再将结果通过HTTP响应返还给客户端.ap ...

  6. Three.js入门篇(一)创建一个场景

    上一面讲述了向场景中添加物体对象.这一篇准备把每个功能点细细的讲述一遍,一方面是为了加深自己的理解.另一方面希望能够 帮助到有需要的人. 一.在学习WEBGL的时候,你应该先了解要创建一个WebGL程 ...

  7. ByValue和ByPropertyName的区别

    ByValue 以Get-Service命令为例,它的帮助文件中关于-Name参数是这样解释的: 1. "WinRM" | Get-Service 2. "Trusted ...

  8. 微信小程序 功能函数 把数字1,2,3,4换成春,夏,秋,冬

    let season =‘1,2,3’; // console.log(season.length) if (season){ if (season.length==1){ seasonChe1=se ...

  9. 错误“AxImp.exe”已退出,代码为 -1163019603【转载及个人看法】

    http://blog.csdn.net/duguduchong/article/details/17166123 最近使用vs2010  在重新生成解决方案的时候出现 “AxImp.exe”已退出, ...

  10. 一条慢SQL引发的血案

    直接切入正题吧: 通常来说,我们看到的慢查询一般还不致于导致挂站,顶多就是应用响应变慢不过这个恰好今天被我撞见了,一个慢查询把整个网站搞挂了先看看这个SQL张撒样子: # Query_time: 70 ...